Tuesday, March 23, 2021

Adventures in the Life of an Engineering Manager: From Developer to Software Manager

Throughout my career I’ve developed a number of interesting engineering solutions that would be worth discussing. One in particular that stands out is the solution to “how would you structure a system where shared content (with a relational structure) can be created by users across their multiple devices in addition to supporting both an online and offline capacity?” While this would show engineering innovation and communications skills, it was quite a while back and doesn’t reflect my current career trajectory. What is more recent and more relevant is the management principles I’ve reflected upon that comprises the basis for my management style. Another factor in choosing this concept is that while it helps guide my daily decision making, this isn’t something that really fits on a resume.

Into the Fire

I had been programmer for a while and would usually be the go-to guy for questions in many situations. I was ready for the next step and wanted to see how I would do managing a team. Being a developer is rewarding and something I never want to completely be away from, but keeping on top of the latest trends and always being at the top of my game required a lot of energy.  What's attractive about management was that the promotion path is both easier and better paid. But would I like it. Was that who I was? Well, I wanted to find out and was able to convince enough folks at one particular company that they were willing to give me a shot at a tech-lead position. A tech-lead position is being responsible for the timelines of a project, and maybe also for developers (or more) but without having any actual HR (Human Resources) authority. Basically, it's having the responsibility of a manager without being in control of the traditional HR part (reviews/hiring/pay rates/etc). Usually you'll have the influence, but not the actual authority. If something goes wrong though, you'll certainly hear about it.

Background

Midway through the first year of managing a team at Comcast I had received feedback from one of my employees that I wasn’t consistent in my decision making. There’s a lot of incoming requests to sort through on a daily basis and maybe one day a particular project might seem more important than another. Consistency is important because it helps to provide psychological safety which an important component to being happy at work. So, I started to think about who I wanted to be as a manager. My goal was to come up with my five most important values so I could make better and more consistent decisions, communicate these values with employees, and hope these values could provide direction for their decisions in cases where I wasn’t available.
A bonus for having these values in a priority order was that it could provide a foundation for making decisions throughout the day. I could use it to cut out cruft and perhaps provide order to the chaos of daily managerial life at Comcast. By choosing five I could focus on a different one each weekday.

Inverted Pyramid of Themes

Themes

What would be the most important theme? Well, at the end of the day any engineering solution needs to serve a customer, and without a customer there is no functioning business, so this one was easy. The first value would be Customer Experience. What can I do each day to serve the customer? This could be to help with a customer issue, review customer feedback, resolve a coding problem, contribute to a meaningful feature, etc.

To serve customers you need employees. Due to preferential treatment for other teams and a tight department budget, the Android team had a history of turnover that I wanted to address. We didn’t want to have to rotate in new employees to replace existing unhappy employees. A better solution would be to keep those employees we already had from wanting to leave. Developers that have growth opportunities or like what they do, don’t want to leave. Growth and Developer Experience would be the second value. Growth for those that want to improve their skills and get promoted and make the day-to-day experience better for those that may already be high performers that are happy where they are and don’t want to change. For growth I would focus on making sure employees had clear path for the next step in the career that I and they would advance. Examples: working on critical projects, putting in positions to be noticed by senior leadership, having a clear list of measurable goals for the year in line with company principles. For developer experience some things I would focus on would be: efficient meetings and processes; an open environment for regular feedback; ensuring the tools developers needed were available, effective, and efficient; and that the teammates worked well with each other.

One of my personal goals is to lead a world class engineering team. Now that we’ve established customer and employees, one important component of a world class team is doing great work that you are proud of. The way to do that is to focus on Excellence. The team would be made aware that they are expected deliver excellent work. Measuring would be a little tricky but one simple method is to track and limit defects that are released into testing or production. Another would be keeping track of code-health in terms of how easy it is to understand and change the code after six months.

In the mobile world and Comcast in general, things move pretty fast. We need to have a good architecture base to handle new product requests in a timely manner. Team members would need to think about Vision and how they can prepare the product for change or better position the team for future needs. Team members would be challenged to demonstrate solutions for large scale features, innovate with new technologies, develop reusable modules, and help make future solutions easier.

Finally, if we’re doing well with all the above items, then we should be able to be trusted to provide features when we say we can have them ready. We want our commitments to matter, so Delivery would be the last value. Team members are expected to compare their expectations with what actually happens so that we can have a predictable and feasible workload.

I've Got So Much Work To Do, Where Do I Start?

Now that we've identified our management themes, we'll be able to look at incoming work and requests to see if it aligns with our themes (and subsequently is important), or whether it doesn't align and maybe is even just a distraction and can be done later.

This brings us to part 2 which is to build a daily routine and triage process for incoming requests that allows us to meet the most important needs and ignore others. Management doesn't have to be an anxiety driven free-for-all with sleepless nights. It's a job that can be rewarding and enjoyable. The simple fact is you can't please everyone and if you try you'll likely end up needing counseling and anti-anxiety drugs. No one is superman and that's not sustainable over a long period of time. If you're in a place where that's expected of you all the time, my advice is to either change that expectation or find a new place to work for your long-term survival and happiness as a manager. Accept that you have to pick and choose who gets the team's precious attention. Not every request is going to make the cut and that's OK. 

Part 2: Let's look at how to apply the Eisenhower Principal using these themes

Monday, October 9, 2017

Android - Programmatically Register System Broadcast Receiver at Runtime

The Problem - Registering connectivity change when targeting API 24+


Not all Android Broadcasts are created equally and you can't create them in the manifest in all cases. There are some you must configure at runtime because of changes Google made to apps targeting N (API 24).

From the documentation: Apps targeting Android 7.0 (API level 24) and higher do not receive CONNECTIVITY_ACTION broadcasts if they declare their broadcast receiver in the manifest.

The Solution


The docs are not as clear how to handle this, so here is the code to allow your app to still receive connectivity change broadcasts after targeting N or later:

public void onCreate() {
    ...
    //Change 'YourConnectionChangedBroadcastReceiver' to the class defined to handle the broadcast in your app
    registerReceiver(new YourConnectionChangedBroadcastReceiver(), new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
    ...
}

I'd also recommend removing the declaration from AndroidManifest.xml as if you leave it in there, it will actually create multiple instances of your broadcast receiver.

        <receiver android:name="YourConnectionChangedBroadcastReceiver">
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
        </receiver>


Registering it programmatically unlocks the receiver for use by your app (including those for CONNECTIVITY_ACTION in your manifest), and that's all there is to it. You can save the registered receiver instance and unregister it when no longer needed, however, this is not necessary if it lasts the entire lifetime of your application.


Saturday, June 25, 2016

Everything You Know is Wrong!

The Background


Test your backwards IQ in this indie trivia game.  I developed this game in Unity3D with a friend of mine, Don.  We came up with the idea of having a trivia game where you would know all the answers but all of the logic is reversed.  The result is quite fun and is especially challenging in cases where what is being asked is negative.  For example: Don't click on the red circle. 

Filling out questions to tempt the user into a wrong answer was almost as fun as playing the game.  The sliding bar quickly disappears giving the user a sense of urgency and you will make mistakes playing this game.

The Game


What you know won't help you, because every answer you know is wrong.  Some times it's not as easy to answer as you think.



Check out the game and let me know you feedback.  Is it good, does the game annoy you?   Whatever.  Check it out and have some fun with it.

Thursday, April 14, 2016

Break Down Data Into a Tree of Categories - Easy Category Tree Breakdown

When you are looking at data it can be helpful to place it into categories based using what I call a category tree breakdown.  This can give us additional information that may not be apparent without looking at the breakdown.

In this simple example, say we have gathered a simple dataset on various political candidates, including: First Name, Last Name, Party Affiliation, Gender, State Affiliation, and Job.  Maybe we would want to see which genders were associated with which jobs or what the similarities are across party or state affiliations. 

Each of these could be done individually in SQL, but that would be inefficient and wouldn't expose the power of being able to perform a breakdown on the fly.

The Dataset in PHP

$rows = array(
    array('fname' => 'Ted', 'lname' => 'Cruz', 'party' => 'Republican', 'gender' => 'male', 'state' => 'Texas', 'job' => 'Senator'),
    array('fname' => 'John', 'lname' => 'Kasich', 'party' => 'Republican', 'gender' => 'male', 'state' => 'Ohio', 'job' => 'Governor'),
    array('fname' => 'Donald', 'lname' => 'Trump', 'party' => 'Republican', 'gender' => 'male', 'state' => 'New York', 'job' => 'Chairman'),
    array('fname' => 'Hillary', 'lname' => 'Clinton', 'party' => 'Democrat', 'gender' => 'female', 'state' => 'New York', 'job' => 'Secretary of State'),
    array('fname' => 'Bernie', 'lname' => 'Sanders', 'party' => 'Democrat', 'gender' => 'male', 'state' => 'Vermont', 'job' => 'Senator'),

);
All I need to do to perform a breakdown is to supply the function with the full dataset and specify which keys to look at to start building categories.  The order of the breakdown is important and determines the structure of the tree that is generated as a result.

For example, let's say I want to look at party affiliation.  Then for each party I want to know what are the positions held by those candidates.  Then for each of those jobs I want to see which states have an affiliation.
That can be done by specifying the breakdown array as follows:

$breakdown_arr = array('party', 'job', 'state');
show_breakdown($breakdown_arr, $rows);
I like to use named keys in my array because that makes it easier to read than integer indexes.
When this breakdown is applied to the dataset, the following result displays:

        Republican
            Senator
                Texas
            Governor
                Ohio
            Chairman
                New York
        Democrat
            Secretary of State
                New York
            Senator
                Vermont


Specify Any Type of Breakdown


The breakdown can be specified in any order we want.  Say we want to look again at the party affiliation, but this time by gender and first name:

$breakdown_arr = array('party', 'gender', 'fname');
show_breakdown($breakdown_arr, $rows);
This shows me a completely different take on the data.  We see here that while the Republican party is dominated by males, the Democratic party is split evenly by gender.

        Republican
            male
                Ted
                John
                Donald
        Democrat
            female
                Hillary
            male
                Bernie



The Algorithm For Performing the Breakdown


function get_array_breakdown($breakdown_indexes, $rows, &$data = null) {
    if (count($breakdown_indexes) < 1) {
        return array();
    }
    if (!$data) {
        $data = new datum();
    }
    $metric_index = array_shift($breakdown_indexes);
    $to_add = array();
    foreach ($rows as $row) {
        $val = $row[$metric_index];
        $to_add[$val][] = $row;
    }
    //Now loop through the children to continue the breakdown
    foreach ($to_add as $key => $to_add_val) {
        $datum = new datum();
        $datum->name = $key;
        $datum->data = $to_add_val;
        $data->addChild($datum);
        get_arrays_for_breakdown($breakdown_indexes, $to_add[$key], $datum);
    }
    return $data;
}

The way this code works is that it continually slices off a category index to look for in the dataset.  Then it loops through the passed rows and puts them into groups based on their values for the index.  These newly categorized values (which are stored in the $to_add array) need to be again gone through for the next breakdown (if there is one) and so are passed along to the next iteration so the next breakdown can be checked.  The remaining breakdown list, the unchecked values that were just added, and the $data object containing the result are all passed back into the method as a recursive call.

When there are no more rows to be added and subsequently checked, the recursive call stops being made and the resulting $data object is returned to the original calling function.

Unfortunately this call can be expensive when many rows and categories are being parsed.  If you have 'n' rows and 'm' columns to review so the big O(n) notation and where k is the number of distinct entries for each category is something like O(n) = n+k^m.

Try this algorithm to find interesting facts on your own datasets.

Complete Example For Performing Category Tree Breakdowns in PHP


<?php
//first name, last name, position
$rows = array(
    array('fname' => 'Ted', 'lname' => 'Cruz', 'party' => 'Republican', 'gender' => 'male', 'state' => 'Texas', 'job' => 'Senator'),
    array('fname' => 'John', 'lname' => 'Kasich', 'party' => 'Republican', 'gender' => 'male', 'state' => 'Ohio', 'job' => 'Governor'),
    array('fname' => 'Donald', 'lname' => 'Trump', 'party' => 'Republican', 'gender' => 'male', 'state' => 'New York', 'job' => 'Chairman'),
    array('fname' => 'Hillary', 'lname' => 'Clinton', 'party' => 'Democrat', 'gender' => 'female', 'state' => 'New York', 'job' => 'Secretary of State'),
    array('fname' => 'Bernie', 'lname' => 'Sanders', 'party' => 'Democrat', 'gender' => 'male', 'state' => 'Vermont', 'job' => 'Senator'),
);

$breakdown_indexes = array('party', 'job', 'state');
show_breakdown($breakdown_indexes, $rows);
/*
Output:
    For breakdown in form of: ["party","job","state"]
    Breakdown result is:

        Republican
            Senator
                Texas
            Governor
                Ohio
            Chairman
                New York
        Democrat
            Secretary of State
                New York
            Senator
                Vermont
*/
$breakdown_indexes = array('party', 'gender', 'fname');
show_breakdown($breakdown_indexes, $rows);
/*
Output:
    For breakdown in form of: ["party","gender","fname"]
    Breakdown result is:

        Republican
            male
                Ted
                John
                Donald
        Democrat
            female
                Hillary
            male
                Bernie
*/

function show_breakdown($breakdown_indexes, $rows) {
    $new_data = get_array_breakdown($breakdown_indexes, $rows);
    echo 'For breakdown in form of: '. json_encode($breakdown_indexes).PHP_EOL;
    echo 'Breakdown result is: '.PHP_EOL;
    echo $new_data;
    echo PHP_EOL;

    return $new_data;
}

function get_array_breakdown($breakdown_indexes, $rows, &$data = null) {
    if (count($breakdown_indexes) < 1) {
        return array();
    }
    if (!$data) {
        $data = new datum();
    }
    $metric_index = array_shift($breakdown_indexes);
    $to_add = array();
    foreach ($rows as $row) {
        $val = $row[$metric_index];
        $to_add[$val][] = $row;
    }
    //Now loop through the children to continue the breakdown
    foreach ($to_add as $key => $to_add_val) {
        $datum = new datum();
        $datum->name = $key;
        $datum->data = $to_add_val;
        $data->addChild($datum);
        get_arrays_for_breakdown($breakdown_indexes, $to_add[$key], $datum);
    }

    return $data;
}

class datum {
    public $name = '';
    public $data;
    public $children = array();

    private $depth = 0;

    public function addChild($child) {
        $child->depth = $this->depth + 1;
        $this->children[] = $child;
    }

    public function __toString() {
        $str = $this->toStringHelper();
        foreach ($this->children as $child) {
            $str .= $child;
        }

        return $str;
    }

    private function toStringHelper() {
        $str = str_pad(' ', $this->depth * 4);
        $str .= $this->name;
        $str .= PHP_EOL;

        return $str;
    }
}

Tuesday, April 12, 2016

Working with D3 - Having an AJAX Loader Image

While D3 makes it easy to get started adding interesting visualizations to your web application and there are a lot of great examples on https://github.com/mbostock/d3/wiki/Gallery.  The problem is that almost none of these examples are ready for prime-time without some serious tweaking.  Either they need to be converted from using static data to relying on AJAX calls, or more importantly they do not support updates.

One suggestion is to use the excellent http://nvd3.org/ library by individuals who have taken the time to address these issues and provide a product that is production ready.

Another change you may need is to provide some kind of indication to the user while data is being loaded into the visualization.

Preparing for adding a loader - CSS code


First you will need to prepare an image to be used as your loading image.  One with a transparent background would likely work best.

In your style.css or whatever you are using for your style-sheet, place the following code that specifies the positioning of the loading image and opacity D3 svg container.  Also, you'll probably want to specify a minimum size of the container that will hold the D3 chart so the loading image knows where to position itself.

    div.ajax_loading {
        position: relative;
    }

    /* Grab only the direct descendant of the div that is an image */
    div.ajax_loading > img {
        position: absolute;
        left: 0;
        top: 0;
        right: 0;
        bottom: 0;
        margin: auto;
    }

    /* Mark all children svg objects as being 50% transparent */
    div.ajax_loading svg {
        opacity: 0.5;
    }

    /* It is best to size your chart so the loader always knows where to display */
    /* An id specifier '#' was used here */
    #performance_chart {
        min-width: 400px;
        min-height: 300px;
    }


Add the Loading Image to the HTML code


Now you will need to add the 'ajax_loading' class to the HTML DIV that will contain the D3 visualization.  Within that DIV you will add the image.

    <div id="performance_chart" class="ajax_loading">
        <img src="img/ajax-loader.gif"/>
    </div>

This will display the loading image by default and also show a nice dimming effect on any svg objects contained in the target DIV.

The image I used has an opaque background, so it only looks right if you are dimming the DIV as we are doing here.  Otherwise you may want to use one with a transparent background.

Loading Image


Add Javascript Code to Control the Loading Image


You'll need to activate and deactivate the loading image from whatever code you are using to call and load the data.  In my case, before every AJAX call I display the loader and once the data is received I hide the loader using the following code.

    function setLoadingIndicator(id, status) {
        d3.select("#" + id).classed("ajax_loading", status);
        d3.select("#" + id + " img").classed("hidden", !status);
    }

Example program:

    //Show loading indicator
    setLoadingIndicator("chart_id", true);
    //Make the call to get data
    $.ajax({
        ...
        success: function(data) {
            //Draw the D3 chart
            draw_chart("chart_id", data);
            //Hide loading indicator
            setLoadingIndicator("chart_id", false);
        },
        error: function(result) {
            setLoadingIndicator("chart_id", false);
        }
    }); 


And that's all there is to it.

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.