{"id":13394399,"url":"https://github.com/bartonhammond/snowflake","last_synced_at":"2025-03-30T10:03:54.600Z","repository":{"id":44380946,"uuid":"47422110","full_name":"bartonhammond/snowflake","owner":"bartonhammond","description":":snowflake: A React-Native Android iOS Starter App/ BoilerPlate / Example with Redux, RN Router,  \u0026 Jest with the Snowflake Hapi Server running locally or on RedHat OpenShift for the backend, or a Parse Server running locally or remotely on Heroku","archived":false,"fork":false,"pushed_at":"2019-03-07T19:03:55.000Z","size":30278,"stargazers_count":4595,"open_issues_count":19,"forks_count":612,"subscribers_count":130,"default_branch":"master","last_synced_at":"2025-03-23T08:12:05.628Z","etag":null,"topics":["async-storage","bitrise","eslint","fastlane","hapi-server","i18n","immutablejs","jest","key-mirror","parse-server","react-native","react-native-router-flux","react-native-vector-icons","redux","snowflake","standard-js","tcomb-form-native","validate"],"latest_commit_sha":null,"homepage":"http://bartonhammond.github.io/snowflake/snowflake.js.html","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bartonhammond.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-12-04T18:34:22.000Z","updated_at":"2025-03-23T03:45:47.000Z","dependencies_parsed_at":"2022-07-14T12:30:39.117Z","dependency_job_id":null,"html_url":"https://github.com/bartonhammond/snowflake","commit_stats":null,"previous_names":[],"tags_count":61,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bartonhammond%2Fsnowflake","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bartonhammond%2Fsnowflake/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bartonhammond%2Fsnowflake/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bartonhammond%2Fsnowflake/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bartonhammond","download_url":"https://codeload.github.com/bartonhammond/snowflake/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246301997,"owners_count":20755514,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async-storage","bitrise","eslint","fastlane","hapi-server","i18n","immutablejs","jest","key-mirror","parse-server","react-native","react-native-router-flux","react-native-vector-icons","redux","snowflake","standard-js","tcomb-form-native","validate"],"created_at":"2024-07-30T17:01:18.123Z","updated_at":"2025-03-30T10:03:54.573Z","avatar_url":"https://github.com/bartonhammond.png","language":"JavaScript","readme":"Snowflake ![snowflake](https://cloud.githubusercontent.com/assets/1282364/19871941/447b11ea-9f85-11e6-81d6-cb4b70faea6f.png)\n==================================\nA React-Native starter mobile app, or maybe just an example, or maybe a boilerplate (you decide) for iOS and Android with a single code base, with 2 backends to chose from: a Hapi or Parse Server solution- [Demo](#screens)\n\n[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/c072e4c80b2e477591170553b149772b)](https://www.codacy.com/app/bartonhammond/snowflake?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=bartonhammond/snowflake\u0026amp;utm_campaign=Badge_Grade)\n[![Join the chat at https://gitter.im/bartonhammond/snowflake](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/bartonhammond/snowflake?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/bartonhammond/snowflake/blob/master/LICENSE)\n[![React Native](https://img.shields.io/badge/react%20native-0.41.2-brightgreen.svg)](https://github.com/facebook/react-native)\n\n## Installation\n\n* [Install React-Native](https://facebook.github.io/react-native/docs/getting-started.html#content)\n\n### Install Snowflake\n* Clone snowflake: `git clone https://github.com/bartonhammond/snowflake.git`\n\n* install dependencies\n\n```\ncd snowflake\n\nnpm install\n```\n\n### Using Snowflake Hapi Server\n\n#### Use the local or remote Snowflake Hapi Server\nTo make things easy for you, the `config.example.js` has been initialized to use the remote **Snowflake Hapi Server** which is running on **Redhat OpenShift**.\n\nThis **Snowflake Hapi Server** is Open Source.  It can run either locally or on **RedHat OpenShift**.  For your convince a server is running at: [https://snowflakeserver-bartonhammond.rhcloud.com](https://snowflakeserver-bartonhammond.rhcloud.com/)\n\nPlease refer to [https://github.com/bartonhammond/snowflake-hapi-openshift](https://github.com/bartonhammond/snowflake-hapi-openshift) for more information about the code and instructions for installation and setup of the server.\n\n#### The following commands are for the client\n\n*  Copy the ```src/lib/config.example.js``` to ```src/lib/config.js```.  \n* **Note**: the `.gitignore` includes `config.js` from being committed to GitHub\n* **Note**: you must select either  `hapiLocal` or `hapiRemote` for the ```backend``` as shown below with `hapiRemote` set as the default.\n\n```\n  backend: {\n    hapiLocal: false,\n    hapiRemote: true,\n    parseLocal: false,\n    parseRemote: false\n  },\n```\n* To run Hapi locally, follow the instructions at [https://github.com/bartonhammond/snowflake-hapi-openshift](https://github.com/bartonhammond/snowflake-hapi-openshift).  You will have to install **MongoDB** and **Redis**.\n* **Note**: The default is to run remotely on the **RedHat OpenShift Snowflake Server** so there is nothing more to do if you want to use it! In that case, just use the `config.js` as is.\n* If you want to install and run  the **Snowflake Hapi Server** locally, then update the ```src/lib/config.js``` file as shown below.  \n* **Note**: use the ip from the `ifconfig` command for the `local`. This ip matches the **Snowflake Hapi Server** setup.\n*  An example of the `url` is shown below assuming the `ifconfig` shows the local ip to be `192.168.0.5`\n* **Note**: You don't have to provide the `local.url` value if you are using the `remote`\n\n```\n  HAPI: {\n    local: {\n      url: 'http://192.168.0.5:5000'\n    },\n    remote: {\n      url: 'https://snowflakeserver-bartonhammond.rhcloud.com/'\n    }\n  }\n\n```\n\n### Using Parse Server\nThis **Snowflake Parse Heroku Server** is Open Source.  It can run either locally or on **Heroku**.  For your convince a server is running at: [https://snowflake-parse.herokuapp.com/parse](https://snowflake-parse.herokuapp.com/parse)\n\nPlease refer to [https://github.com/bartonhammond/snowflake-parse-heroku](https://github.com/bartonhammond/snowflake-parse-heroku) for more information about the code and instructions for installation and setup of the server.\n\n#### The following instructions are for the client\n* Copy the ```src/lib/config.example.js``` to ```src/lib/config.js```.  \n* **Note**: the `.gitignore` includes `config.js` from being committed to GitHub\n* Set `parseLocal` to true if you are running a local instance of parse-server\n* Otherwise, set `parseRemote` to true to indicate your parse server instance is hosted in the cloud\n\n```\n  backend: {\n    hapiLocal: false,\n    hapiRemote: false,\n    parseLocal: true,\n    parseRemote: false\n  },\n```\n\n* To setup parse-server, follow the instructions at https://github.com/ParsePlatform/parse-server-example\n* Set the `local.url` value if you are running parse-server `local`\n* Set the `remote.url` value if you are running parse-server `remote`\n\n```\n  PARSE: {\n    appId: 'snowflake',                              // match APP_ID in parse-server's index.js\n    local: {\n    \turl: 'http://localhost:1337/parse'             // match SERVER_URL in parse-server's index.js\n    },\n    remote: {\n    \turl: 'https://enter_your_snowflake_host.com'   // match SERVER_URL in parse-server's index.js\n    }\n  }\n\n```\n\n### To run:\n* For iOS, from the command line, run via command: ```react-native run-ios``` or open XCode and load project, Run ```Product -\u003e Run (⌘+R)```\n* For Android, from the command line, run via the command: ```react-native run-android``` assuming you have an emulator or device running and attached\n* To run Jest, ```npm test```\n* To debug Jest unit cases, install [node_inspector](https://github.com/node-inspector/node-inspector) and run ```npm run test-chrome```\n* Enjoy!\n\n\n----------\n\n\n------------\n## Notes\n\nCode is written to [JS Standard](https://github.com/feross/standard) and validated with [Eslint](http://eslint.org/).  To setup your favorite editor using the Eslint configuration, see [Editors](http://eslint.org/docs/user-guide/integrations#editors)\n\nNavigation is handled with [React Native Router Flux](https://github.com/aksonov/react-native-router-flux).  Multiple scenes support **Login, Register, and Reset Password**.  Once successfully logged in, there are 3 more scenes: **Logout, Subview, and Profile**.\n\nA user can **change** their **Email Address** and **User Name** once they are logged in using the **Profile** form.\n\nThe icons used throughout the app are from [React Native Vector Icons](https://github.com/oblador/react-native-vector-icons), namely using **FontAwesome**\n\n**Form building** is extremely easy and consistent by using [Tcomb Form Library](https://github.com/gcanti/tcomb-form-native) by using **domain models** and writing less code.\n\nUsing [Redux](https://github.com/reactjs/react-redux) and [Immutable](https://facebook.github.io/immutable-js/), the state of the application is **testable** with [Jest](https://facebook.github.io/jest/), which includes [Snapshot tests](http://facebook.github.io/jest/blog/2016/07/27/jest-14.html) currently with 85 tests and ~90% coverage!!!  \n\nTo ease the pain of Redux Action definitions, Snowflake uses [Key Mirror](https://github.com/STRML/keyMirror).\n\nUsing the [Validate.JS](https://validatejs.org/) Library, all **user input is validated**.  Appropriate messages are displayed to the user guiding them in the input requirements.\n\nOnce a user is logged in, their **Session State is stored** in [AsyncStorage](https://github.com/jasonmerino/react-native-simple-store) so that subsequent usage does not require logging in again.\n\nSnowflake supports **multiple languages** using [I18n](https://github.com/AlexanderZaytsev/react-native-i18n) with English, French and Spanish.\n\nSnowflake supports **Hot Reloading** of its state.  \n\nSnowflake uses CI with [Bitrise.io]( https://www.bitrise.io) and has **extensive docs and 45+ min of video** demonstating implementation.\n\nSnowflake has a **choice of servers**, either\n\n* **Hapi Server** that runs on **RedHat Openshift** and **locally**.\n\n    See [https://github.com/bartonhammond/snowflake-hapi-openshift](https://github.com/bartonhammond/snowflake-hapi-openshift) for more information about the OpenShift Hapi server.  The setup instructions below describe how to select the server you desire.  \n\n* **Parse Server** that runs **remotely** or **locally**\n\n    See [https://github.com/ParsePlatform/parse-server-example](https://github.com/ParsePlatform/parse-server-example) for more information.\n\n---------------\n# Content\n\n- [Editor Configuration](#editor-configuration)\n- [Screens](#screens)\n- [Summary](#summary)\n- [Quotes](#quotes)\n- [Technologies](#technologies)\n- [Continuous Integration](#continuous-integration)\n- [Redux State Management](#redux-state-management)\n- [Hot Reloading](#hot-reloading)\n- [FAQ](#faq)\n- [Source documentation](http://bartonhammond.github.io/snowflake/snowflake.js.html)\n\n----------\n\n## Editor Configuration\n**Atom**\n```bash\napm install editorconfig es6-javascript javascript-snippets linter linter-eslint language-babel\n```\n\n**Sublime**\n* https://github.com/sindresorhus/editorconfig-sublime#readme\n* https://github.com/SublimeLinter/SublimeLinter3\n* https://github.com/roadhump/SublimeLinter-eslint\n* https://github.com/babel/babel-sublime\n\n**Others**\n* [Editorconfig](http://editorconfig.org/#download)\n* [ESLint](http://eslint.org/docs/user-guide/integrations#editors)\n* Babel Syntax Plugin\n\n## Screens\n\n| Platform| Register     | Login | Profile   |\n| :------:| :-------: | :----: | :---: |\n| iOS|  ![iOS Profile](https://cloud.githubusercontent.com/assets/1282364/11598478/b2b1b5e6-9a87-11e5-8be9-37cbfa478a71.gif)  | ![iOS Login](https://cloud.githubusercontent.com/assets/1282364/11598580/6d360f02-9a88-11e5-836b-4171f789a41d.gif)| ![iOS Register](https://cloud.githubusercontent.com/assets/1282364/11598582/6d392750-9a88-11e5-9839-05127dfba96b.gif)  |\n| Android |![Android Register](https://cloud.githubusercontent.com/assets/1282364/11598579/6d3487b8-9a88-11e5-9e95-260283a6951e.gif)    | ![Android Login](https://cloud.githubusercontent.com/assets/1282364/11598577/6d2f140e-9a88-11e5-8cd4-1ba8c9cbc603.gif)   |  ![Android Profile](https://cloud.githubusercontent.com/assets/1282364/11598578/6d314ee0-9a88-11e5-9a6c-512a313535ee.gif) |\n\n\n----------\n\n## Summary\n\n1. The application runs on **both iOS and Android** with a **single code** base\n1. A User can **Register, Login, Logout, Reset their Password** and modify their **Profile**\n1. The Forms display messages for **help and field validation**.\n1. The Forms are **protected** when fetching.\n1. The Forms display **spinner** when fetching.\n1. Form submission **errors are displayed** (see above Login)\n1. **All state changes*** are actions to the Redux store.\n1. The backend is provided by either a Hapi server or Parse server using the **Rest API**\n1. **Every action** performed by the UI interfaces with the **Redux actions** and subsequently to the Redux Store.  This **reduces the complexity** of the JSX Components **tremendously**and makes them easily testable.\n1. **Jest Unit Tests cover ~90%** of the application statements.\n1. Demonstrates how to **setup React-Native to perform Jest testing** with Mock modules\n1. Includes ability to **debug Jest unit tests** with Chrome\n1. Instructions and videos for **continuous integration with Bitrise.io**\n\n----------\n## Quotes\n\nSome quotes from users of **Snowflake**\n\n**Open Source Mag: Learn best of React Native with these open source projects**:\n[http://opensourceforu.com/2016/05/learn-best-of-react-native-with-these-open-source-projects/](http://opensourceforu.com/2016/05/learn-best-of-react-native-with-these-open-source-projects/)\n\n**ICICletech Blog: Mobile App Development With 8 Awesome React-Native Starter Kits**:\nWe have listed some of our favorite starter kits and boilerplates to get started quickly.\n[https://www.icicletech.com/blog/react-native-starter-kits](https://www.icicletech.com/blog/react-native-starter-kits)\n\n**Infinite.Red: Ignite Your Mobile Development:**\n\u003e awesome releases as Barton Hammond’s snowflake.\n\n[https://shift.infinite.red/ignite-your-mobile-development-32417590ed3e#.pz7u3djtm](https://shift.infinite.red/ignite-your-mobile-development-32417590ed3e#.pz7u3djtm)\n\n**AdtMag: New Community Projects for React Native: Deco IDE and Pepperoni Boilerplate**\n[https://adtmag.com/articles/2016/05/26/react-native-projects.aspx](https://adtmag.com/articles/2016/05/26/react-native-projects.aspx) Snowflake mentioned\n\n**Pepperoni App Kit** (see [Credits](https://github.com/futurice/pepperoni-app-kit#credits))\n\u003eThis project was initially motivated by Snowflake....you should check it out to see if it's a good fit for your app.\n\n**Viktor**\n\u003eJust saw the tweets, still watching the vids. It's awesome!! It's really really high quality, I'm truly amazed\n\n**John**\n\u003eproject is awesome, thank you for providing it!\n\n**Eric**:\n\u003e I've been going through snowflake and love what you have done!\n\n**Nikos**:\n\u003e wow new videos, nice\n\n**Josh**\n\u003ethanks for the thorough videos!\n\n**Patrick**\n\u003e just wanna snowflake is awesome\n\n**Justin**\n\u003eCongrats - the project is super helpful\n\n**Stephen**\n\u003eThanks so much for this amazing foundation!\n\n**Jim**\n\u003eNeat project\n\n----------\n\n## Technologies\n*The following are brief descriptions of the technologies used*\n\n### [React-Native](https://facebook.github.io/react-native/)\n*React Native enables you to build world-class application experiences on native platforms using a consistent developer experience based on JavaScript and React.*\n\nWhat more can I say?  It's a fantastic leap forward in providing the ability to write native applications with Javascript that target both iOS and Android.\n\nThis application provides one code base that works on both platforms.  It demonstrates Form interactions,  Navigation, and use of many other components.\n\n### [Jest](https://facebook.github.io/jest/)\n*85 Unit tests that cover plain objects and JSX components*\n\nThe de-facto standard for React/Native testing.  This app demonstrates how to mock **ReactNative, node_modules, classes** and to properly **test JSX components** by programmatically changing the props, and throughly **test the applications data state and the actions** in conjunction with Redux.\n\n![Jest Coverage Analysis](https://cloud.githubusercontent.com/assets/1282364/17187737/19524234-5400-11e6-8350-53e653a4c1f6.png)\n\n### [Redux](http://redux.js.org/)\n*Redux is a predictable state container for JavaScript apps. It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test.*\n\nBefore Redux, application state was managed by all the various components in the app.  Now, the state is managed in a **predictable and consistent manner** and it can be **tested with Jest** and best of all, it is **independent** of the UI.  This is a major advancement in application design!\n\n### [Hapi](http://hapijs.com/)\nFor me, Hapi provided a clear way for defining APIs and managing things clearly.  Some folks like Express, I've used Express myself, and I wanted to try Hapi.  I'm very satisfied with Hapi.  As you may or may not know, WalMart Labs produced Hapi and they've used it in their business.  \n\nOne of the needs of any application is server side processing.  With the ability to run Hapi locally or on OpenShift, I'm able to write my server logic and test it locally.  When I'm \"happy\" I can push the code to OpenShift.  The same code runs in both environments.\n\n### [Parse Server](https://github.com/ParsePlatform/parse-server)\nAs an alternative to Hapi, Snowflake also supports Parse Server.  Parse Server is an open source version of the Parse backend that can be deployed to any infrastructure that can run Node.js.\n\nParse Server works with the Express web application framework.  You can test it locally and push changes to your parse remote server when you are ready.\n\n### [OpenShift](https://www.openshift.com/)\nI chose OpenShift because I could get a reasonable performing application for free.  The Snowflake server ([https://github.com/bartonhammond/snowflake-hapi-openshift](https://github.com/bartonhammond/snowflake-hapi-openshift) uses 3 gears with MongoDB and Redis.  \n\n### [TComb](https://github.com/gcanti/tcomb-form-native)\n*A structured model based approach to declarative forms*\n\n*With this library, you **simplify form processing** incredibly!  Write a lot **less code**, get **usability** and **accessibility for free** (automatic labels, inline validation, etc) and no need to update forms when domain model changes.  This application **demonstrates how to test these forms** also!\n\n### [Validate.js](http://validatejs.org/)\n*Validate.js provides a declarative way of validating javascript objects.*\n\nUsing Validate.js for the Form processing was a breeze!  And with the ability to test the validations to the Redux state was very easy!\n\n---------------\n\n\n## Continuous Integration\n\nCI proves to the developer that everything required to build and test\nthe application is well defined and repeatable.  Without CI, one would\nnot know, for a fact, that all the required tools and assests are\navailable for everyone to build with.  CI gives us developers some\n\"peace of mind\" that our build process is repeatable.\n\nThe following videos will walk you through setting up CI with BitRise.io\n\n- [Introduction](#introduction)\n- [Bitrise Overview](#bitrise-overview)\n- [iOS XCode Modifications](#ios-xcode-modifications)\n- [XCode Certs Provision Profiles  App Id](#xcode-certs-provision-profiles-app-id)\n- [Create iOS App](#create-ios-app)\n- [iOS install](#ios-install)\n- [Android Setup](#android-setup)\n- [Addendum #1](#addendum-#1)\n- [Things not addressed](#things-not-addressed)\n\n----------\n\n##### Introduction\n* **Video 1/7**: [https://youtu.be/EYafslJvXz8](https://youtu.be/EYafslJvXz8)\n\n\u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=EYafslJvXz8\" target=\"_blank\"\u003e\n  \u003cimg src=\"http://img.youtube.com/vi/EYafslJvXz8/0.jpg\" alt=\"Introduction\" width=\"240\" height=\"180\" border=\"10\" /\u003e\n\u003c/a\u003e\n\n* Snowflake is a *starter app* so all tutorials are basic in nature\n* There are a bizzilion ways of doing any of this - I'm showing one\n* There's a number of CI sites, I chose Bitrise.io\n* There's a general understanding of why to us a CI\n* The build will be done on Bitrise.io - not locally\n* The **only** goal is to get the build to run on Bitrise.io\n* You can **still** run applications locally with simulators\n\n\n##### Bitrise Overview\n  * **Video 2/7**: [https://youtu.be/JAHlfNUKoLg](https://youtu.be/JAHlfNUKoLg)\n\n\u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=JAHlfNUKoLg\" target=\"_blank\"\u003e\n  \u003cimg src=\"http://img.youtube.com/vi/JAHlfNUKoLg/0.jpg\" alt=\"Bitrise.io Overview\" width=\"240\" height=\"180\" border=\"10\" /\u003e\n\u003c/a\u003e\n\n  *  Introduction to Bitrise.io [https://www.bitrise.io/](https://www.bitrise.io/)\n  * Overview of what it does for us\n  * Overview of the two WorkFlows\n\t  * iOS\n\t  * Android\n\n##### iOS XCode Modifications\n* **Video 3/7**: [https://youtu.be/3y72adWNRSU](https://youtu.be/3y72adWNRSU)\n\n\u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=3y72adWNRSU\" target=\"_blank\"\u003e\n  \u003cimg src=\"http://img.youtube.com/vi/3y72adWNRSU/0.jpg\" alt=\"iOS XCode Modifications\" width=\"240\" height=\"180\" border=\"10\" /\u003e\n\u003c/a\u003e\n\n  * XCode\n\t* Icons - Asset Catalog Creator Free - see App Store\n\t* Bundler ID\n\t* [ https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/ConfiguringYourApp/ConfiguringYourApp.html](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/ConfiguringYourApp/ConfiguringYourApp.html)\n\t* AppDelegate.m\n\t* Versioning\n\t* [https://developer.apple.com/library/ios/qa/qa1827/_index.html](https://developer.apple.com/library/ios/qa/qa1827/_index.html)\n\t* Automatic Certificate \u0026 Development\n\n##### XCode Certs Provision Profiles App Id\n  * **Video 4/7**: [https://youtu.be/zJXoHIaJg7Y](https://youtu.be/zJXoHIaJg7Y)\n\n\u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=zJXoHIaJg7Y\" target=\"_blank\"\u003e\n  \u003cimg src=\"http://img.youtube.com/vi/zJXoHIaJg7Y/0.jpg\" alt=\"XCode Certs Provision Profiles\" width=\"240\" height=\"180\" border=\"10\" /\u003e\n\u003c/a\u003e\n\n* see [https://developer.apple.com/library/ios/qa/qa1814/_index.html](https://developer.apple.com/library/ios/qa/qa1814/_index.html)\n* App Development Center\n\t*  Remove all Certs, App Ids, and Profiles\n   * Generating  Certificates and Provision Profile\n\t\t* Update XCode appDelegate.m w/ ifconfig value\n\t\t* Xcode -\u003e Run with device attached\n\t\t* Select option for Xcode to fix signing\n\n##### Create iOS App\n* **Video 5/7**: [Bitrise, YML, Profile, P12](https://youtu.be/olfpwEjVlZ4)\n\n\u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=olfpwEjVlZ4\" target=\"_blank\"\u003e\n  \u003cimg src=\"http://img.youtube.com/vi/olfpwEjVlZ4/0.jpg\" alt=\"Create iOS App\" width=\"240\" height=\"180\" border=\"10\" /\u003e\n\u003c/a\u003e\n\n* Login/Register Bitrise.io\n* Dashboard\n* Add App\n* Authenticate GitHub\n* Select repository, branch\n* Import YML\n* Download Certifications\n* Update KeyChain\n* Save .p12\n* Download Provision\n* Load .p12 and provision to Bitrise.io\n* Setup Secret and Env Vars\n* Build\n\n##### iOS install\n* **Video 6/7**: [https://youtu.be/nQWXJI0ncns](https://youtu.be/nQWXJI0ncns)\n\n\u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=nQWXJI0ncns\" target=\"_blank\"\u003e\n  \u003cimg src=\"http://img.youtube.com/vi/nQWXJI0ncns/0.jpg\" alt=\"iOS install from email\" width=\"240\" height=\"180\" border=\"10\" /\u003e\n\u003c/a\u003e\n\n* From device, open email app\n* Open **letsconnect**\n* Follow instructions to load in Safarie\n* Follow prompts and enjoy!\n\n##### Android Setup\n* **Video 7/7**: [Android YML, Setup](https://youtu.be/o4RQZodbzIU)\n\n\u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=o4RQZodbzIU\" target=\"_blank\"\u003e\n  \u003cimg src=\"http://img.youtube.com/vi/o4RQZodbzIU/0.jpg\" alt=\"Android Setup\" width=\"240\" height=\"180\" border=\"10\" /\u003e\n\u003c/a\u003e\n\n* Settings -\u003e Docker Image\n* Secret Variables\n* App/build.gradle\n* Setup Secret and Env Vars\n* Import YML\n* Run build\n\n##### Addendum #1\n* **Video**: [XCode Edge Stack, WorkFlow](https://youtu.be/mP5D2MQboxw)\n\n\u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=mP5D2MQboxw\" target=\"_blank\"\u003e\n  \u003cimg src=\"http://img.youtube.com/vi/mP5D2MQboxw/1.jpg\" alt=\"XCode Edge Stack\" width=\"240\" height=\"180\" border=\"10\" /\u003e\n\u003c/a\u003e\n\n* \"XCode Edge Stack\" for iOS\n* New React-Native steps for Install \u0026 Bundle\n* Upgraded Step versions\n* GitHub branch triggers\n\n\n\n##### Things not addressed\n* Submission to any store\n* Working with other CIs\n* Complex setups for either iOS or Android\n* Debugging any failures\n* Shiny new things\n* No local builds with Bitrise CLI\n* No local builds with Fastlane\n* No tutorial on all the features of Fastlane\n\n\n---------------\n\n## Redux State Management\nThis section explains a little about what I've learned with Redux and the management of application state.\n\n### Without Redux\n\n![Alt text](https://g.gravizo.com/source/custom_mark10?https%3A%2F%2Fraw.githubusercontent.com%2Fbartonhammond%2Fsnowflake%2Fmaster%2FREADME.md)\n\n\u003cdetails\u003e \n\u003csummary\u003e\u003c/summary\u003e\ncustom_mark10\n  digraph G {\n    aize =\"4,4\";\n    login [shape=box];\n    login -\u003e validate [style=bold,label=\"1\"];\n    login -\u003e restful [style=bold,label=\"2\"];\n    restful -\u003e parse [style=bold,label=\"3\"];\n    login -\u003e router [style=bold,label=\"4\"];\n    router-\u003e new_page [style=bold,label=\"5\"];\n  }\ncustom_mark10\n\u003c/details\u003e\n\n\nA typical app, at least those I've written before, have logic and state scattered all through out.  Frameworks help to organize but typically the developer has to know how to \"stitch\" it together by remembering to update certain state at certain times.\n\nIn the above diagram, the Login component is responsible for calling the Validate and making sure the rules are applied and errors are displayed. Then it has to have logic that confirms everything is in the right state, and pass that data to a Restul API to interact, in this case, with Parse.  When that activity is complete the Router has to be informed and a new_page can be displayed.\n\nThis makes testing hard as the logic is partially contained in the Login component itself and the sequence of those events are encapsulated there too!  \n\n### With Redux\n\n![Alt text](https://g.gravizo.com/source/custom_mark11?https%3A%2F%2Fraw.githubusercontent.com%2Fbartonhammond%2Fsnowflake%2Fmaster%2FREADME.md)\n\n\u003cdetails\u003e \n\u003csummary\u003e\u003c/summary\u003e\ncustom_mark11\n  digraph G {\n    aize =\"4,4\";\n    login [shape=box];\n    login -\u003e Action[style=bold,label=\"1\"];\n    Action -\u003e Reducer [style=bold,label=\"2\"];\n    Reducer -\u003e login [style=bold,label=\"3\"];\n    login -\u003e Action [style=bold,label=\"4\"];\n    Action -\u003e restful [style=bold,label=\"5\"];\n    restful -\u003e parse [style=bold,label=\"6\"];\n    Action -\u003e Reducer [style=bold,label=\"7\"];\n    Reducer -\u003e new_page [style=bold,label=\"8\"];\n  }\ncustom_mark11\n\u003c/details\u003e\n\nWhat the above diagram depicts is that the logic for login is contained in the Actions and Reducer and can be tested in isolation from the UI. This also makes the UI Component Login much simpler to write as it just renders content based on props that are provided.  The props are actually the Reducer Store.\n\nSo what is happening in this diagram?  Where's the Validation?  That would be in step 1.  When the user enters characters on the form, they are sent to an action.  The action packages them up and sends them to the Reducer.  The reducer looks at what Type the Action is and performs the operation, in this case, Validate the parameters.\n\nHow does Login respond?  Well, it recieves the updated Props - the properties which are the store.  It then renders it's output based on those properties.  Just straight forward logic - nothing more then checking the properties and display approptiately.  At no time does the Login component change any property state.  \n\nThe responsibility of changing state belongs to the Reducer.\n\nEvery time an Action is called, that Action interacts w/ the Reducer.  The Reducer does state changes on the State or Store model.\n\nLet's look at a concrete example, Resetting a Password.  Now the UI is pretty simple, provide a Text Input for the email entry and a Button to click.  Let's further assume that the email has been validated and the user clicks the \"Reset Password\" button.  All that Login does is call an Action function called ```resetPassword``` passing the email value.\n\nSo don't get too overwhelmed with the following code.  You can think of the ```dispatch``` calls as meerly as a messaging system.  Each ```dispatch``` call invokes a function - it just calls a function in a Promise chain.  \n\nI want to now look at one specific Action and trace it's path.  That action is the ```dispatch(resetPasswordRequest());``` call.  That call signals that the \"**reset password request**\" processing is starting.  We'll look at that next and how it trickles all the way back to the Login ui.\n\n\u003e Action.resetPassword\n\n```\nexport function resetPassword(email) {\n  return dispatch =\u003e {\n    dispatch(resetPasswordRequest());\n    return new Parse().resetPassword({\n      email: email\n    })\n      .then((response) =\u003e {\n        if (response.status === 200 || response.status === 201) {\n          dispatch(loginState());\n          dispatch(resetPasswordSuccess());\n        } else {\n          dispatch(resetPasswordFailure(JSON.parse(response._bodyInit)));\n        }\n        return response;\n      })\n      .then((response) =\u003e {\n        if (response.status === 200 || response.status === 201) {\n          dispatch(emitLoggedOut());\n        }\n      })\n      .catch((error) =\u003e {\n        dispatch(resetPasswordFailure(error));\n      });\n\n  };\n}\n```\n\nThe simple function returns an object with type ```RESET_PASSWORD_REQUEST```.  All of the activities performed have this pattern, make a REQUEST and then either have a SUCCESS or a FAILURE.\n\nNow Redux is responsible for taking this returned Action object and passing it to the Reducer.  What does the Reducer do with this Action?  We'll look at that next.\n\n\u003e Action.resetPasswordRequest\n\n```\nexport function resetPasswordRequest() {\n  return {\n    type: RESET_PASSWORD_REQUEST\n  };\n}\n```\n\nThe Reducer is a function with 2 parameters, state and action.  What it does is to make modifications to the state depending upon the action.  The state is any javascript object you like.  This application uses Immutable.js for it's state so to guarantee the state is always immutable.\n\nNotice the ```switch``` statement - it's looking at that Action.type value - all Actions have a type value.  In our case, the type is RESET_PASSWORD_REQUEST.\n\nSo what's happening here.  If any request type comes to the Reducer, including RESET_PASSWORD_REQUEST, we change the state by setting the ```form.isFetching``` to ```true``` and the ```form.error``` field to null.  The returned new state value has these two fields set.\n\nWhat happens with that new state?  Well Redux makes that state available in the Login component ```props```.  And since the ```props``` have changed, Login needs to ```render```.  Let's look at that next.\n\n\u003e Reducer.Action.ResetPasswordRequest snippet\n\n```\nexport default function authReducer(state = initialState, action) {\n  if (!(state instanceof InitialState)) return initialState.mergeDeep(state);\n  switch (action.type) {\n\n  case SESSION_TOKEN_REQUEST:\n  case SIGNUP_REQUEST:\n  case LOGOUT_REQUEST:\n  case LOGIN_REQUEST:\n  case RESET_PASSWORD_REQUEST:\n      let nextState =  state.setIn(['form', 'isFetching'], true)\n        .setIn(['form','error'],null);\n    return nextState;\n.....\n```\n\nSo here in the ```render``` component we need to decide if a ```spinner``` should be displayed.  It should display when the ```props.isFetching``` is ```true```.  That's the value set above in the Reducer!  All the Login ```render()``` has to do is check that property and set the UI appropriately.\n\n\u003e Login.Header.render snippet\n\n```\n  render() {\n    let spinner = \u003cText\u003e \u003c/Text\u003e;\n    if (this.props.isFetching) {\n      spinner =  \u003cGiftedSpinner/\u003e;\n    }\n\n    return (\n      \u003cView style={styles.header}\u003e\n        \u003cImage style={styles.mark} source={{uri:\n                                            'http://i.imgur.com/da4G0Io.png'}}\n        /\u003e\n        {spinner}\n      \u003c/View\u003e\n    );\n  }\n```\n\nHopefully this gives you some insight into how Redux is integrated into the application.  In my opinion, this is a huge step in application logic.\n\nIf you'd like to read an excellent tutorial on Redux I can't recommend this one enough: [http://teropa.info/blog/2015/09/10/full-stack-redux-tutorial.html](http://teropa.info/blog/2015/09/10/full-stack-redux-tutorial.html)\n\n\n--------------------\n\n## Hot Reloading\nThis video shows Snowflake exporting and importing state from Redux.  It demonstrates, with the iOS Simulator, the process of copying the state for import at a later time.  After the demo, I walk through the code to clarify how I achieved this.  It's assumed you have some familiarity with Redux.  Hopefully it helps you gain a better understanding of what Redux provides you!\n\n\u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=b4eqQUA3O6o\" target=\"_blank\"\u003e\u003cimg src=\"http://img.youtube.com/vi/b4eqQUA3O6o/0.jpg\"\nalt=\"Snowflake Hot Loading\" width=\"240\" height=\"180\" border=\"10\" /\u003e\u003c/a\u003e\n\n\n\n-------------\n\n## Faq\n\n### How do I change the Language of the App?  \n\nJust use the Settings of your Device or Simulator.\n\n### Why did you use the RestAPI instead of the (JS-sdk | ParseReact)\n\nI looked at it initially and, imo, it had too much magic.  For example, I couldn't see how I was going to isolate my JSX components easily and keep all my \"state\" in Redux.  ParseReact is right there in the middle of your JSX component which would tie me to a very particular vendor and implementation detail.  If I decided to later move away from ParseReact I'd have to rewrite a lot of code and tests.\n\nAlso, it had this statement\n\n\u003e Mutations are dispatched in the manner of Flux Actions, allowing updates to be synchronized between many different components without requiring views to talk to each other\n\nI don't want to deal w/ wrapping my head around Flux Actions and have to monkey-patch or something to get Redux Actions.\n\nIn a previous life, I worked with Parse JS SDK and it's based on backbone.js.  So I didn't go that direction either, because, again, I didn't want to have another data model to deal with.  Plus, at the time I was using it, the SDK was buggy and it was difficult to work with.\n\nWith the Parse Rest API, it's simple, can be tested itself from the command line with curl, it's clear, it's succinct and it's easily replaced with something else, an example such as Mongo/Mongoose without much, if any, impact on the code base.\n\n\n###### -barton hammond\n","funding_links":[],"categories":["Seeds","JavaScript","开源App","Misc","Marks","Index","Uncategorized"],"sub_categories":["Other Platforms","React Native","[React Native - A framework for building native apps using React](https://facebook.github.io/react-native)","Continuous Integration / Deployment / Delivery","🌎 [React Native - A framework for building native apps using React](facebook.github.io/react-native)","Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbartonhammond%2Fsnowflake","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbartonhammond%2Fsnowflake","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbartonhammond%2Fsnowflake/lists"}