Showing posts with label Ionic. Show all posts
Showing posts with label Ionic. Show all posts

Sunday, January 17, 2016

Ionic Angular App as a Website Front-end - AJAX/CORS solutions

Ionic and Angular are great frameworks for building single page web apps.  If you want to create an app that you can then deploy on almost any mobile device out there based on HTML5 and Javascript (JS), then you are starting in a good place.

If you are just building an app that does not communicate to other websites and deals only with information stored locally or already on the app, then you will have no issues.  What the guides do not explain well is all the issues you will have communicating with other websites.  These are JS features (or limitations) that protect users from malicious js code.  Native Android (dalvik) and iOS (Objective-C/Swift) apps do not have that limitations, so native developers may be unfamiliar with these restrictions.

This article will help you identify and solve some common pitfalls when building an ionic app.  The source code to the sample ionic app is available on github.

Typical architecture for an Ionic App as a web site front-end.

 

Make sure your server allows remote access by Javascript:


If you are communicating to another server, you will need to set it up to allow for Cross-Origin Resouce Shared (CORS) access or JS will not be able to read the responses.  If the server you want to talk to is not your own and does not have Access-Control-Allow-Origin in its response headers set to where you need it (generally '*' or 'file://'), then you'll need to find a different way such as a proxy or a native app.
In Apache's httpd.conf file, you will need something like this on the directory you want to allow access to:
<Directory "/your_www_dir">
  #You may prefer "file://" instead of "*" because it is more restrictive
  Header set Access-Control-Allow-Origin "*"
  ...
Or this can be done in your host language.  For PHP:
//When testing app locally, it will be run from localhost:8100
$client_origin = "";
if (array_key_exists('HTTP_ORIGIN', $_SERVER)) {
    $client_origin = $_SERVER['HTTP_ORIGIN'];
}
if (stristr($client_origin, 'localhost') !== false) {
    $origin = "http://localhost:8100"; //test host
} else {
    $origin = "file://";
}
//Allow only from two specific locations: testing, and from JS app
header("Access-Control-Allow-Origin: $origin");
//To be less restrictive, you can use this line below
//header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");

Make sure your Ionic project has whitelists enabled and configured:


Newer versions of Ionic projects seem to have this enabled by default, but if you do not have the whitelist plugin already, you will need to install it.

Check if the whitelist plugin is installed:
$ ionic plugin list
cordova-plugin-console 1.0.2 "Console"

...
cordova-plugin-whitelist 1.2.0 "Whitelist"

If not, install it:
$ ionic plugin add https://github.com/apache/cordova-plugin-whitelist.git
Configure the app's config.xml file to allow access to outside sites (i.e. beyond 'file://'), add after <content src="index.html"/>.  Add the last two lines to allow Android/iOS apps to open sites other than you own (if desired):
<access origin="*"/>
<allow-intent href="*"/>
<allow-navigation href="*"/>

For iOS apps, check your App Transport Security (ATS) settings:


When you do an app build, a platforms/ios/{app_name}/{app_name}-Info.plist file is created.  If your app ONLY accesses HTTPS, you won't need to set this.  Otherwise add the bolded lines to the file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs$
<plist version="1.0">
  <dict>
    ...
    <key>NSAppTransportSecurity</key>
    <dict>
      <key>NSAllowsArbitraryLoads</key>
      <true/>
    </dict>

  </dict>
</plist>
If this is not set, you may have communication issues and see the following error:
App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.

Load your code or try the sample project:


The sample project uses the Angular $http service to communicate to a sample website.  I use http://espn.go.com as an example because they send an Access-Control-Allow-Origin = '*' and allows us to test our app.  You can see the response in the Message section and any errors in the Error section.


iOS app publishing tips


Getting an app running in the Apple App Store has its own set of challenges.  Check out these Ionic iOS App Publishing tips: Publishing an Ionic Angular App for iOS - The Hidden Steps & Pitfalls.


Something you should never ever do - have Allow Origin set to * and Allow Credentials to true



According to the spec you can't have Allow Origin = "*" and Allow Credentials set to "true".  There is a really good reason for this.  It prevents malicious Javascript from using your credentials saved in cookies against you.  Say you logged into a website and it knows who you are through a cookie.  If in another window you ran some malicious JS code and both of these settings were enabled, the JS code could look like a logged in user to the remote server.  Not so bad if it is a sports website, but horrendous if it is your banking site.  With all that said there is a way to do this in Apache.  Just echo the given origin back to the user.  The browser never sees it as Allow Origin=* so it allows the request.

This code in your httpd.conf completely exposes your users to malicious JS code:

   <Directory "/somedir">
       SetEnvIf Origin "^(.*)$" ORIGIN_SUB_DOMAIN=$1
       Header set Access-Control-Allow-Origin "%{ORIGIN_SUB_DOMAIN}e" env=ORIGIN_SUB_DOMAIN
       Header set Access-Control-Allow-Credentials "true"





Publishing an Ionic Angular App for iOS - The Hidden Steps & Pitfalls



The manual for Ionic makes it seem so easy to get started - and it generally is... that is until you go to publish your app to the Apple store and run on real devices.  It can be hard getting started if you don't know you need any of these crucial elements.  Later versions of Ionic already include some of these steps making future builds easier.  Here is a list of some solutions to pitfalls I've run across publishing Ionic apps to the Apple App Store.


Make sure you have your provisioning license




In using iOS, you'll need to create an AppId and with it an associated provisioning license at developer.apple.com.  This needs to match the widget id in the Ionic config.xml.

Create and Set the widget id for a provisioning license developer.apple.com, then update config.xml:
<widget id="com.company_name.app_name" version="x.x.x" ...

Make sure the system can find your provisioning license


In my system it initially was not able to find the license.  So, I needed to perform this step in order to properly deploy
$ sudo cp -r ~/Library/MobileDevice/ /Library/
If this is not set correctly, you can get the following error when deploying your app to a device:
Check dependencies
Code Sign error: No provisioning profiles found: No non–expired provisioning profiles were found.
** BUILD FAILED **

When accessing non-HTTPS endpoints, check your App Transport Security


When you perform ionic build ios, a platforms/ios/{app_name}/{app_name}-Info.plist file is created.  If your app ONLY accesses HTTPS, you won't need to set this.  Otherwise add the bolded lines to the file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs$
<plist version="1.0">
  <dict>
    ...
    <key>NSAppTransportSecurity</key>
    <dict>
      <key>NSAllowsArbitraryLoads</key>
      <true/>
    </dict>

  </dict>
</plist>
If this is not set, you may have communication issues and see the following error:
App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.

Building your app in Xcode


In order to send your app to the Apple App Store, you will need to load your app in Xcode.  When you perform ionic build ios it creates a file in your platforms/ios/{app_name}.xcodeproj file that you open in Xcode.

In my version of the project I needed to make some modifications to the project in order to build.  Under Build Settings / Search Paths / Header Search Paths, I added:
$(OBJROOT)/UninstalledProducts/$(PLATFORM_NAME)/include
To both Debug & Release for Any SDK.


Select the correct orientation and display settings


Under General / Deployment Info, Select Add device Orientations.  Do not select a Main Interface - this should be left blank.  Make sure you have selected all desired orientations and 'Requires full screen'.  It will put these keys in the plist file, although that does not seem to be the only location they are required because setting these without doing so in Xcode does not set the values properly.
    <key>UIRequiresFullScreen</key>
    <true/>
    <key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>

Error if Main Interface .xib is selected:
2015-10-28 08:29:57.260 AppName[89398:1651075] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UIApplication 0x787193c0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key view.'

Setup the custom icons for the app


Set your icon & splash screen in the resources folder.  Use a minimum of 192x192 for the icon and 2208x2208 for the splash screen (it will be cropped for portrait/landscape).  Run ionic resources to automatically compile the icons into your project so it will appear on the device:
$ ionic resources
This section came from help from: http://blog.ionic.io/automating-icons-and-splash-screens/

Export the app from Xcode and upload the app to the Apple App Store


Initially the Project / Archive will not be available.  Next to triangle play button and the square stop button, the current device your can deploy to is selected.  Click the area to change this from iPhone to Generic iOS device.  It does not look like an input combo box, but you can click on it to change it.

Click here to change the deploy target and activate the Archive selection under the Product menu


You'll need to export the project to an archive to publish to iTunes connect and then run have processed through the app store.  Open Xcode.  Select Project / Archive from the Xcode menu.


Dealing with Cross-Origin Resource Sharing


Getting communications working to remote sites can have many potential issues to be worked out.
Check for my full post on settings needed for making CORS requests: Ionic Angular App as a Website Front-end - AJAX/CORS solutions.