Category: Code

Scaling Up with Expo for React Native Mobile Apps

Expo is a great delivery system for getting React Native mobile apps off the ground and into the hands of users. If I were to do it all over again, here is what I would have loved to know ahead of time!

Words!

Standalone App = A native app file built by Expo for iOS (ipa) or Android (apk) that is ready to upload to the respective app stores.

App Store = Either Apple’s App Store or Google Play for Android

Publish = Ah this does not mean publishing to the app stores. This means an over-the-air update. This term is accurate for Expo because your app becomes immediately available to use for users in the Expo App Client itself.

OTA = Over The Air, meaning a live update of your app content and functionality via the internets. Like serving a webpage.

Build = Means a process by which Expo uploads your code to its servers (publish) and then runs a build process in the Cloud that gives you a link to download the final product.

Your Expo Account = Your App Ecosystem

An Expo Account is *the* account that stores and serves your app. It is not a user account! Publishing your apps under this account essentially locks it to the username, and trying to switch to a different account later can cause problems. For example I identified that switching your account and republishing the same app to the same release channel will cause all the previously saved AsyncStorage user data (iOS UserDefaults) to be inaccessible.

Also if you do get into a bind of transitioning off to a new Expo account, you will end up having to annoyingly maintain apps on an old Expo login and duplicate efforts to publish your app. This is because Accounts essentially own the ecosystem which is explained later.

SecureStore vs AsyncStorage

The solution to the above problem is to use SecureStore to persist values in the user’s phone keychain. This even persists data after the user has deleted their app. SecureStore does have character limits though and I wouldn’t trust it to deal with large amounts of object data, so it should only be important key-value pairs. AsyncStorage is still important for persisting app session state, but essentially it should be treated as if the data could be wiped tomorrow.

Use Release Channels

Do it now or feel the pain later. The hierarchy of Expo is this:

  • Expo Account
    • Expo App
      • Release Channel
        • iOS/Android App

As noted above, if you ever have to switch accounts, you will end up maintaining this entire ecosystem TWICE. Your “production” release channel is different between each account!

I recommend creating new release channels for each standalone app version update too. This is because Expo often comes out with new and awesome SDK version upgrades which require a new build. The new SDK version might have code changes or features that either are not compatible or do not exist in the previous versions. A new build is also required when adding new app permissions, loading screen stuff, or tablet support.

And I won’t get into it, but you’ll see how even more complicated things get if you decide to splinter iOS and Android into separate release channels.

Just Let Expo Update the App

Expo provides an option to disable Over-the-Air updates (OTA) and provides a nice API to administer Updates yourself. This seems easy at first but can get you in trouble later.

  • Expo has had issues whereby the notification of a new Update could continue to flag even after the user has updated, creating an endless reload loop.
  • Finding a way to lock the user down to an updated version has lots of pitfalls. What if the very code to determine if the user needs to download the Update is itself broken? You have bricked their app!
  • Publish rollbacks do not resolve the above problem because you have already told Expo not to automatically update the app. It has essentially updated itself into oblivion!
  • App.json settings can not be rolled back to automatic updates because it is already integrated into the build that is live in the App Store.
  • Expo always shows its own error pages, overriding your componentDidCatch. So you couldn’t even provide an escape route or user prompt for a bricked app.

Don’t worry about it! Are updates really that big? If you’re pushing minor updates now and then the download time is minimal. And if you’re doing massive code changes, you’re better off just re-building the app for store publishing. Plus your app is basically an Expo wrapper to begin with, so you’re not really saving that much time for the convenience.

App Store Updates are Still Important

Firstly, app store updates are also meaningless. Assuming you didn’t generally tweak the app.json, you could push out app store versions 1.1, 1.2, and 1.3 and they could all read a single “production” release channel. A single publish would push to all users regardless of version. In Expo, the app.json “version” is your arbitrary versioning scheme, only “sdkVersion” is critical. Meaning it’s marketing fluff for users, much like how Google Play uses versionName vs versionCode.

I already noted some use cases by which an app store update is required. However, the magic really comes when we talk about OTA updates that fail. If an OTA update fails, meaning Expo can’t get the newest hottest thing you published, it will automatically fallback to its “bundled” state. This means Expo will serve from cache the app at the time you built it.

When disabling OTA updates and having bundled assets, you basically mimic typical native app development where each version update is a snapshot build. With OTA updates enabled, you still want an emergency cushion if Expo can’t serve the newest stuff. Under the hood, Expo will continue to try and update the user and they should see new stuff on the next app open.

Takeaways

Expo and React Native have definitely solved the problem of fast development and relative ease of delivery. However long-term maintenance and deployment of live code is still an issue. There is for example no UI by which one can easily track release channels or which users are seeing them, you really have to memorize them and provide good analytics reporting. The Expo website is a pretty basic listing of your apps and recent builds.

Overall if your team is investing in React Native over native, this is the platform to go with but do maintain robust internal tracking of your releases.

Expo build command throws errors

When working with different SDKs of Expo, switching the existing repo will cause watchman and various caches to be out of sync. Running the various clearing commands doesn’t seem to resolve it.

The build process is best done when you run a cache cleared instance with expo start -c and then in a separate window, run the build command you need.

Sending Angular $http POST Data to PHP

In the case of sending form data in Angular, $http POST data information is serialized as “application/json” which is not interpretable by PHP.

You have to transform the request (the form data object) into a format that it understands. The easiest way is to configure the $httpProvider.

/* Because PHP sucks */
myApp.config(['$httpProvider', '$httpParamSerializerProvider', function($http, $httpParamSerializerProvider) {
    var paramSerializer = $httpParamSerializerProvider.$get();
    $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
    $http.defaults.transformRequest = function(data) {
        return paramSerializer(data);
    };
}]);

 

Code Examples: CSS Squares

Back in the 2000s, creative agency websites really liked the grid effect where you would hover over a block in a grid and that block would expand into some content.  Of course then, people would use Flash or some complicated jQuery contraption.  I created my own code example of this for a client once to demonstrate a CSS3 based design working with typical HTML element flow instead of overriding everything with position absolute and calculating block sizes on the fly.   This example also serves to teach how powerful CSS inheritance can be to create series of movements that look like fancy Javascript.

It’s not a perfect example because if a user mouseovers too quickly to the next block, the widths may exceed the row and break to the next line.  A solution could be to give some extra spatial buffer to the .main-content wrapper.   jQuery is still used here to trigger the next child block to be “hidden.” Since the transitions are set the same, one smoothly replaces the other.   At the end of the row, it triggers the previous child.

Topic 1

It’s not a perfect example because if a user mouseovers too quickly to the next block, the widths may exceed the row and break to the next line.  A solution could be to give some extra spatial buffer to the .main-content wrapper.   jQuery is still used here to trigger the next child block to be “hidden.” Since the transitions are set the same, one smoothly replaces the other.   At the end of the row, it triggers the previous child.

Topic 2

It’s not a perfect example because if a user mouseovers too quickly to the next block, the widths may exceed the row and break to the next line.  A solution could be to give some extra spatial buffer to the .main-content wrapper.   jQuery is still used here to trigger the next child block to be “hidden.” Since the transitions are set the same, one smoothly replaces the other.   At the end of the row, it triggers the previous child.

It’s not a perfect example because if a user mouseovers too quickly to the next block, the widths may exceed the row and break to the next line.  A solution could be to give some extra spatial buffer to the .main-content wrapper.   jQuery is still used here to trigger the next child block to be “hidden.” Since the transitions are set the same, one smoothly replaces the other.   At the end of the row, it triggers the previous child.

How to Quickly Backup and Restore MAMP’s phpMyAdmin Databases

It took me some Googling to nail down the question “Where does MAMP’s phpMyAdmin (PMA) store mysql databases in OSX?” so I thought I’d summarize my findings.  The reason mysqldump isn’t as convenient is that MAMP is purposely GUI-focused, so switching brains and trying to navigate Terminal to MAMP’s mysql core is annoying and not intuitive (MAMP stores things all over the place).  Also mysqldump doesn’t easily like dumping all databases.  If we’re GUI, then let’s see if there’s a GUI solution and voila there is.

  • MAMP’s PMA stores its database files (.frm) inside HD/Library/Application Support/appsolute/MAMP PRO/db/mysql
  • Simply navigate to that folder and zip up the entire mysql folder (be wary there is also a separately named mysql folder inside this folder, ugh).
  • Throw that onto your Desktop or backup drive.
  • Let’s say you return to a fresh install of MAMP PRO, navigate back to that folder and copy all the files including ibdata1, ib_logfile1, ib_logfile0 which contains all the permissions and sql structure for phpMyAdmin.
  • Restart MAMP (which restarts mysql) and PMA should be refreshed with all your databases like nothing ever happened.

Similarly now you know how to cherry pick from old Time Machine backups instead of needing to setup the MAMP environment all over again.   Of course different versions of PMA may have changed that db folder structure, so be wary of that.

jQuery Handling of CSS Classes

Though CSS3 transition animation support is still a little wonky, it’s getting better every day.  I’ve decided to pass all animation handling to CSS versus previously using jQuery’s fade functions.   jQuery is now simply the trigger, adding and removing classes.

Example: Fading Modal Box

A “modal window” is the technical term given to those annoying pop-ups in Windows.  It’s synonymous with dialog or alert.   In websites, the same concept is typically used to pop-up a larger version of a smaller image, as in a blog post.  For me, they’re useful as “page” content displays for websites which need to remain static, like in use of maps or dashboards.

jQuery

In the example below, a click listener handles the entire operation:

  • .modal-toggle is assigned to the button calling the action. ie: <a href=”#about” class=”.modal-toggle”>Click for About</a>
  • Of course, prevent default link action.
  • variable target references the href URL of the button or link. #about
  • This URL points to a div ID acting as my .modal. ie: <div id=”about”></div>
  • Next I add my “master” css class called .transitional to the .modal div.  I use a master class for applying CSS transition animations because obviously I don’t want to be adding this CSS to every single class that needs it — which increases browser load.
  • Now I’m using the link itself to judge if we’ve clicked on this before.
    • If the link has class active, then do the opposite first.  I don’t like testing negatives for if/else.
      • Remove class active, and remove class active on the modal.  Note we have to queue a delay to hide() a.k.a display: none because otherwise it won’t wait for the transition to finish. I set it to 500 ms ahead of my 400 ms.  See that hide() has a numeral hide(0) because this notifies jQuery to queue that operation; you could conceivably add more queued operations 1, 2, 3, etc.  This is required or delay does nothing.
    • If the link does not have it, let’s do our magic.
      • Add class active, then on the modal add class active.   Note we have to queue a delay on the addClass because otherwise the div will show() a.k.a. display: block immediately without waiting for the addClass.  The fancier queue function is used here because addClass cannot be queued itself.  So we have to nest it inside the available jQuery queue function.  dequeue() is required to again notify jQuery that addClass is a queued operation.   The delay is shorter, I’m just giving the browser time to separate the actions between show() and the addClass.
      • The animation is triggered by addClass(“active”) for the .modal div.
$(".modal-toggle").on("click", function(e) { 
        e.preventDefault();
	var target = $(this).attr("href");
	$(target).addClass("transitional");
	if ($(this).hasClass("active")) { 
		$(this).removeClass("active");
		$(target).removeClass("active").delay(500).hide(0);
	} else { 
		$(this).addClass("active");
		$(target).show().delay(10).queue(function(){
		    $(this).addClass("active").dequeue();
		});
	}
});

CSS

Simple enough!  The CSS is organized as follows:

  •  .modal creates our main control “layer”.  I’ve offset it from the top, assuming that’s where our main navigation link for .modal-toggle exists.  We don’t want to cover that up or we can’t close it.
  • .modal.active triggers our CSS animation to fade opacity to 100%.  That’s all it needs.
  • .content is an additional div layer nested under .modal because in general you should separate information out.  Also this lets you style the actual visible window as if it was centered on the page.
  • .transitional is my master CSS class.
.modal { 
	position: fixed;
	top: 5%;
	left: 0px;
	z-index: 1000;
	display: none;
	opacity: 0;
	width: 100%;
	height: 95%;
	}

.modal.active { 
	opacity: 1;
	}

.modal .content {
	position: relative;
	z-index: 200; 
	width: 80%; 
	height: 80%;
	margin: 0 auto 0 auto;
	background: #fff;
	padding: 20px;
	}

.transitional { 
    -webkit-transition: all 400ms ease-in;
    -moz-transition: all 400ms ease-in;
    -ms-transition: all 400ms ease-in;
    -o-transition: all 400ms ease-in;
    transition: all 400ms ease-in;
}

Thought Process of Working in Google Maps

Sometimes as a front-end you think by taking the “back-end” methodology route, you can accomplish great feats. In the end, the answer is often much simpler than you think. Such as in this example.

I need to find the coordinates of the center of a polygon.

Easy right?

First problem, the polygon is stored in KML format in a Fusion Table. Let’s use my awesome getJSON call from last year to retrieve the data, oh wait, the API has changed, okay let’s go to their ajax example even though it’s technically equivalent. Now the data is an object, oh I can’t work with an object.

Google, google, google…

Let’s try Google visualization since they enjoy offering different ways to find the same carrot. Oh okay I have to load Goog JS. More resources, the better! A dozen for loop iterations later, I still have an object.

Okay let’s try stringifying by manually pulling out the coordinate information from the KML object with js splits, parseFloats and jQ text() conversions. Great now I have 20 lines of coordinate data because it’s a complex polygon.

Except to retrieve the bounds of the polygon, I need a RECTANGLE. Argh! Perhaps if I become a zen master of algorithms, I can determine which four lines of coordinates are the approximate four corners of an averaged rectangle. Easy!

Google, google, google…

Try getCenter, getBounds and anything that gets something out of nothing. Thinking I need to extract the raw KML formatted data as is and then use to to call forth KmlLayer inline. Half an hour later, I realize I’m trying to thrust a packet of data into what is only a URL call.

Maybe I should extract the first coordinate point of each polygon via strlens and use that as an approximated marker location with some math adjustments. Oh wait, every polygon’s first coordinate point is all over the place. Banish the thought.

Google, google, google…

Answering some random questions on StackOverflow that do not relate but came up anyway.

Upon consulting every inch of Google Maps V3 developer guide, I come to realize I just need to zero in on the infoWindow’s self-centered latLng itself.

Several hours and dozens of lines of test code later, I actually needed just one line of code.

Yes sir, you can have labels in the middle of each polygon now.

/die.

CSS Image Swap from Image Crop

I’m often asked by clients and coding learners how to conceptualize the output of HTML/CSS to front-end. Basically these are first steps to fully understanding what the DOM hierarchy is — or in other words, how the browser interprets the document flow and order.

A client was keen to create a CSS class that would produce a image swap with an image tag in a blog post. He would create two images and insert them into a DIV that would “hide” one. On hover, the other half of the image would appear. He knew such a thing could happen and that two divs would possibly be involved but could not determine how the CSS would do this.

The solution is to put both images into a div, we’ll call swapimage, and assign the second image class swap-target.  Here the HTML5 figure tag can be used in place of the div as it works semantically.

<figure class="swapimage">
<img src="image1.png" alt="First Image." />
<img src="image2.png" class="swap-target" alt="Second Image." />
</figure>

And the CSS:

<style type="text/css">
.swapimage {
 display: block;
 position: relative;
 margin: 0;
 padding: 0;
}
.swap-target {
 position: absolute;
 top: 0;
 left: 0;
 display: none;
z-index: 2;
 }
.swapimage:hover .swap-target {
 display: block; }
</style>

Here swapimage acts as a “container” for the image swap.  Relative position allows the second image to absolutely align “over” the other one (set by z-index).  On swapimage:hover, the target image is set to block.   CSS visibility can be used, but I prefer display in most cases, as the difference being it actually removes the element from the page.

Safari File Upload Hang Up

The HTML Form file input tag has been around for ages but for some reason Safari has had native issues with it since the dawn of the Mac era. I always expect there to be issues that arise in every project but this was certainly was a surprise that put me through the early hours of the night before final client testing.

An Ajax hack is required to force Safari to close the keep alive requests which is better explained by techies at Webkit Bugzilla. The hang effect is basically Safari continuing to show it’s uploading a file and never going anywhere. Sometimes the file will work and go through, especially smaller sizes.

Solutions varied on closing keepalive, telling Safari to move it. Airblade Software offers a good solution which is expanded by this freelancer forum I visited. The script requires Ajax and the forum suggested using the Prototype framework’s Ajax support but that would create conflicts with my existing jQuery code so I grabbed the simple Ajax Request Library by Matt Kruse.  As well, a forum user notes you should call the script before the form, thus document ready is added.

In your body’s form, include onsubmit to the kill function.

<form action="upload.php" enctype="multipart/form-data" method="post" onsubmit="closeKeepAlive();">
<input type="file" name="image" />
<input type="submit" id="upload" name="upload" value="Send " />
</form>

In the head:

<script type="text/javascript" src="js/ajaxrequest.js"></script>
<script type="text/javascript">
/* die safari! */
$(document).ready(function () { 
function closeKeepAlive() {
  if (/AppleWebKit|MSIE/.test(navigator.userAgent)) {
    new Ajax.Request("safari-die.php", { asynchronous:false });	
  }
return true;
}
});
</script>

In the php file

<?php header("connection: close"); ?>