Build Mobile iOS, Android, and Web Apps Using React and Ionic, Part II

Write Once Run Anywhere Is Alive And Well In Modern JavaScript Development

This is Part II in a series. Part I described what Ionic is and how to setup a starter project. In this second article I’ll demonstrate how similar Ionic development is to React. And we’ll continue to build out our camera app.

In developing this story I decided to use a photo application, as it demonstrates the hardware interface capabilities of Ionic. Note to keep things simple I’ll always refer to Ionic related components as Ionic, even when the capability may be coming from Stencil or Capacitor. When a distinction would be helpful I’ll be more specific (if you’re not sure what I’m referring to, please read Part I of my story). So let’s layout what we’ll build in some detail before we begin coding. Our app, MyPhotos, will do what most camera focused applications do: allow users to take pictures with the device camera and then display those pictures in a list. For navigation, we’ll use tabs. We’ll have one tab link to a screen that provides our camera access. And we’ll have another tab that uses a list to show thumbnail views of all our app images. We won’t be building a Photos killer, but this exercise should be good enough to help you get a feel for Ionic mobile development on iOS devices. Note I’m skipping Android to make the series a little shorter, but it’s also a fully supported platform.

Getting Started With Code

In order to begin coding with Ionic we need to know what assets are available and how writing code with Ionic actually works. Let’s start doing this by opening our MyPhotos project from Part I and taking a look at the contents of src.

If you open the App.tsx file again you’ll see that our chosen style of UI navigation is tabs (I won’t show the file again as it’s very long and I’ve already displayed it in Part I). Having said that, the actual routing to screens is done with React Router’s Route tags. The components you see like IonReactRouter and IonRouterOutlet are just wrappers to allow Ionic access to React Router services. Now as you look through the folders you’ll see pages and theme. The React components inside pages represent screens on the phone, and those screens are displayed whenever the relevant tab element has been pressed. You might be wondering how url routing ties into a locally running mobile app, but there’s actually no contradiction here. You’ll recall React Router uses virtual routing, that is the routes are not actually existing on a server. They are local to the application and allow the React app to trigger screen loads based upon those url routes. So the Ionic team realized there was no reason to throw out React Router just to do a mobile app. Everything still just works.

Now let’s look at one of the pages files. In the App.tsx file on the first Route tag we see the url “/tab1”, which is associated to the Tab1 component inside the pages folder. Let’s open that file and take a look.

The Tab1 component represents the first screen that loads when you run the command ionic serve to start the app. Let’s go through the code. As stated previously Hooks is supported out of the box so the component is a functional one. Next in the JSX we can see that IonPage is the root container. Every component in the pages folder should start with an IonPage component at its root. Next we have the IonHeader this obviously represents a container for the very top header of the screen. If you have a header that repeats, you can of course put this code into your own header React component and reuse it. Just below IonHeader is the IonContent component; as the name implies this represents the body of your screen. You should have only one IonContent per screen. This component mostly controls scroll related events in the body, for example ionScrollStart, ionScroll, ionScrollEnd — the names are pretty self explanatory of what they do. Note all standard HTML tags that React supports are still supported. But by using the Ionic components you are getting theme-able, pre-built, great looking controls, with lot’s of additional functionality for free. In other words an Ionic React project, is still just a React project. It’s just that in addition to standard React features Ionic components and services have been added. Take a look at all the control choices here. OK, we’re now almost ready to make changes to code but let’s also take a look at the theme folder and styling.

The theme folder contains a file called variables.css. It is an easy to access container for all of the theme related colors that Ionic uses in its component styles. Modifying this file allows you to quickly change the color scheme of your app from a single location (in a later post I’ll get into how to use this file to create a dark theme for your application). The syntax may seem strange, but attributes like “— ion-color-primary” are actually variables, much like the variables inside SASS files. This capability is a newer feature in standard CSS and allows you to define a variable, for example a color, and reuse it throughout your app. It even works inside your JSX style attributes. After defining a variable, you use it by calling var. For example

color: var(--ion-color-primary);

Placing this attribute inside your CSS or style property allows you to reference the variable’s true value by just passing the name. So instead of using #0cd1e8 use a meaningful variable name instead.

In addition to CSS variables Ionic components have a consistent set of properties that can be used as shortcuts to quickly change the style of an element. For example most Ionic components have a property called color. The value of color can be a string that represents one of the variables from the variables.css file. However instead of using the full variable name you use just the last word. So for example if you use

<IonButton color=”secondary”>My Button</IonButton>

your button will receive the color of the variable “ — ion-color-secondary”. Obviously writing that short property is better than doing something like

<IonButton style={{ color: '#1ed7f82' }}></IonButton>

Not only for length, but also readability. Additionally Ionic components are usually aggregate HTML and Web Component elements. So setting for example the style color will not necessarily have the desired effect, because you might be changing the color of a different element than the one you intended. So again, setting the property of the Ionic component is a shorter way of getting the desired style change. Let’s hook into this Ionic capability by creating our own color variable that we will use for emphasizing certain elements. Open variables.css and add the following

--ion-color-accent: #69bb7b;--ion-color-accent-rgb: 105, 187, 123;--ion-color-accent-contrast: #ffffff;--ion-color-accent-contrast-rgb: 255, 255, 255;--ion-color-accent-shade: #5ca56c;--ion-color-accent-tint: #78c288;

Now we need to create a new CSS file in order to add a class that maps to our newly created variables.css entry. So create a file called base.css inside of the theme folder and add

.ion-color-accent {--ion-color-base: var(--ion-color-accent);--ion-color-base-rgb: var(--ion-color-accent-rgb);--ion-color-contrast: var(--ion-color-accent-contrast);--ion-color-contrast-rgb: var(--ion-color-accent-contrast-rgb);--ion-color-shade: var(--ion-color-accent-shade);--ion-color-tint: var(--ion-color-accent-tint);}

We needed to create this class in order for Ionic to recognize a new CSS variable as an Ionic component property. Now let’s go back to the App.tsx file and add our base.css import so that all components can get this class. Type under the variables.css import

import './theme/base.css';

Now let’s open Tab1.tsx and try resetting all the color properties of the bottom IonIcon entries to use “accent”. It will look like this

The project should auto compile and then your screen should have the new color

As you can see the icons under Resources have changed to pink.

Creating Our First Screen

Let’s start by adding a new file called Camera.tsx and write that from scratch. But before we do this we’ll need to install two more packages. The first is pwa-elements. This package is a Web Components package built with Stencil. It provides the interface for the Camera, and other device services, but only for browsers. On mobile devices you will get the native camera interface. The next package is react-hooks. This package exposes much of Capacitors capabilities as hooks. So we will be able to access the camera by using hooks.

npm i @ionic/pwa-elements @ionic/react-hooks

After installing these two packages open the index.ts file and update like below

From pwa-elements we are importing defineCustomElements and then wrapping the window object so that calls can be intercepted. Again, this means at runtime on the browser this will cause any calls to the camera or other supported services to have an Ionic UI presentation. If you don’t do this you will get an error like below, when running in the browser, as the desired interface will not exist.

Next let’s create the Camera.tsx file if we have not done so already. For now just create an empty Camera function that is exported. We’ll fill it in later. Now let’s update the tab element inside of Apps.tsx to point to our new Camera.tsx file. We’ll start from the top of the file. Since Ionic uses the ionicons library for its own icons, we need to update the import statement using ‘ionicons/icons’ to replace the flash icon with the camera icon. Next go down to the first Route element and replace the path property with “/camera” and the component property with Camera. We’ll associate this route to the tab later. Next update the last Route element and change the Redirect to property to be “/camera”, so that the camera screen loads by default. And then on the first IonTabButton let’s change the tab property to be “camera” and href to be “/camera”. Since Ionic has integrated React Router to their own controls this change allows a click on this tab to trigger the “/camera” route. Next change IonIcon’s icon property to be “camera” and IonLabel’s text to be Camera. Once complete you should see this code.

Now let’s start updating the Camera file too. Since this article is getting a bit long let’s just get the camera up and running and in the next part we’ll fix the styling. Add this code into the Camera.tsx file.

Starting from the top of the file it shows we need two new imports: @capacitor/core and @ionic/react-hooks/camera. Capacitor provides the core services to interface into hardware and react-hooks converts those calls into hooks style calls. If you have not enabled Capacitor yet you’ll need to do so first.

ionic integrations enable Capacitor
ionic cap add ios

Moving further down you’ll notice the call to useCamera and the returned properties: photo and getPhoto. GetPhoto initializes the camera instead of retrieving the taken image, which confused me a bit but nevertheless that’s what it does. The photo object is the object that will receive the image data once the actual picture is taken. Depending on the options given to getPhoto it will have the images data and path information or a base64 encoded string representing the image. After getPhoto you can see we call useCallback to memoize the actual call to getPhoto. The resultant triggerCamera call is what is used to trigger the actual initialization of the camera later in the code. After the useCallback call, you can see there is a test for the availability of the getPhoto function, availableFeatures.getPhoto. In the @ionic/react-hooks component there is a separate folder for each device service. In our case, as is shown, we are using the camera. In each service folder there is an availableFeatures call that allows you to test if the device has that capability or not. Since we are working cross platform and across devices, having this sort of testing ability is a good thing. Next the actual call to getPhoto shows that it is passed some parameters when called. The most important of which is the resultType, which we will give the CameraResultType.Uri parameter. Using the Uri type gives us a property called webPath, which we can use on our img tags since html img elements only recognize url strings and not file paths. After that you can see some fairly obvious code like useEffect, which is doing logging of what’s inside the photo object, and the camera button click handler, which again is only initializing the camera. But after this code is the JSX which contains an IonPage root container, another header and of course the IonContent, which has the button and an img tag that will receive the picture once clicked. Now that we’ve set everything up let’s do a run of the app. First we need to build the app again and make sure everything compiles so type the following

ionic build
ionic cap copy ios
ionic cap sync ios
ionic cap open ios

The commands above do the following. First we build the app, with ionic build. Next we copy the updated source files over to XCode. Then we also need to bring over any iOS specific plugins so that Capacitor can connect our web view to the relevant device services. We use sync for that. And finally we open the project. After executing these commands try and run the iOS XCode project on your device (I’ve already gone over how to setup the XCode project in Part I). Here’s a picture of the final screen with an incredible photo of my desk lamp!

The End (for Part II anyway)

Some of you who are not familiar with native coding might find the process a little arduous. Not only did we write code and add imports that were not necessarily obvious. We also had to execute command line calls to make sure that the native project build was always in sync with the web build, which also means we’ll have to do that for every platform we intend to support. However let’s think about the end result here. Without writing a lick of native code. That by the way, we would have otherwise had to write 3 or more times (iOS, Android, Desktop, Web). We managed to get access to the camera across device platforms with only a single code base and using Hooks coding style to boot. To me this is quite amazing.

This is the end of Part II. Source code can be found here, https://github.com/dharric/MyPhotos. In the next section we’ll make our camera screen a little prettier using Cards and some alignment of the image. And we’ll learn about the useStorage hook so that we can save our photos locally and then list them on our second tab with thumbnails.

Build Mobile iOS, Android, and Web Apps Using React and Ionic, Part I

Write Once Run Anywhere Is Alive And Well In Modern JavaScript Development

This article will be the first in a series introducing Ionic 4. And showing how you can keep your existing skill set for the web, to create attractive and fully functional mobile applications.

Originally, I had planned to use React Native for my mobile app. I’m a React developer so it seemed reasonable to assume it would be what I needed. But one of the things that annoyed me right away was the syntax. It was quite different from React for the web. In addition I found some frameworks that I liked don’t work in React Native. Things like standard CSS and React Router are not used in React Native development. So after trying it out I started shopping around for something better. I think Ionic is what I was looking for.


Some of you React devs might be thinking Ionic is an odd choice. Isn’t that the framework for Angular? So I’ll give a quick overview of what Ionic is, in its current v4 iteration. The latest version is a complete rewrite of the framework. It’s actually more like three distinct components than a single framework. At its core is something called Stencil. According to Ionic documentation Stencil is a compiler that emits standard Web Components. But it is also its own coding framework that allows creation of custom Web Components; with no dependency on external frameworks like React or Angular. This decision allows Stencil to be framework agnostic. Next up is the piece known as Ionic. Ionic React, Ionic Angular, Ionic Vue are basically wrappers around Stencil that allow you to code in your desired app framework and still emit the same Ionic components. Think of it like how TypeScript eventually gets converted into native JavaScript. So when using Ionic React you can continue to use SASS, React Router, Redux, etc without issue. It even supports Hooks! And then finally we have Capacitor. Capacitor is a runtime that allows the web view, that your app lives in, to communicate with your device API and access hardware and services (it also does the work of attaching your dev build to the native iOS or Android build system, XCode or Android Studio).


OK so all this sounds cool but what’s the big deal? What this all means is that now you can write your iOS, Android, Web or even Desktop App entirely using a web framework of your choosing. So if you are a React dev you can keep using your favorite npm packages and coding techniques to build mobile apps that look and feel just like native apps. No quasi CSS like object syntax; no HTML wanna-be tags. Just standard React in all its glory. So let’s get a feel for what Ionic development is like by building a small photo app and deploying onto an iPhone. First let’s install Ionic globally.

npm i -g ionic

Once you have Ionic installed globally we can use it like create-react-app to setup our initialized application. Let’s create our app by calling Ionic cli without any parameters.

ionic start MyPhotos
Select React as web framework

As the screenshot shows, the first thing to do is select which web framework you will be using to build your app (note Vue is currently still in beta). Let’s select React and then go to the next step. This step allows us to select which starter template to use to build our app. To allow for simple navigation in our app let’s select tabs.

Select tabs as template

Finally after hitting enter Ionic should start setting up your project. It has to download quite a few packages so this can take a while. Once it completes cd into the MyPhotos directory and type

ionic serve

You should see the app load in your browser, and if you use chrome’s debugger and switch to mobile mode you should see the below.

Tabs navigation based Ionic template

As you can see the built in template is quite attractive and has a theme fairly similar to other native iOS apps. Now let’s take a look at the contents of the MyPhotos directory and see how Ionic has laid out this project.

The layout is a pretty standard React-like folder structure. You have a src folder, index and App files, an npm packages.json file, and a public build folder. Also as is clear by the file extensions Ionic uses TypeScript by default, although this can be disabled. If you open the App file you should see the following.App.tsx file

Again Ionic fully supports hooks without additional updating. If you look at the code you notice the JSX starts with IonApp at the root. Every Ionic app must have the IonApp component at its base. After that you can see the IonReactRouter component. This is a wrapper around React Router that Ionic has created to allow for better integration with their framework, it’s comparable to the BrowserRouter. After that you can see the IonTabs component which will host the individual tabs involved in navigation. And finally you should see the IonRouterOutlet. This component is roughly equal to the Switch component in React Router.

Now that we have a feel for a basic Ionic app I need to explain how we go from this web app to an iOS or Android app. So as previously mentioned Ionic at its core uses only web technologies. There is a clean separation of concerns between Ionic the web framework and the native mobile pieces. So then the question is how do we build an app that works, on iPhone and Android, like a native app? This is where Capacitor comes in. Capacitor is the glue that ties your web app to native services. It wraps your code, so that it can function as a native app. Capacitor also provides the integration to our native development environment, for example XCode on Mac, so that our web app will be compiled into a native binary and can hook into all the native device capabilities that we want.

Note in order to build a native binary we need a computer running XCode. Unfortunately Apple does not provide XCode on non Mac computers so you’ll need a Mac to build your iOS app. However, I do want to be clear on this point, you can develop your app on any platform that allows for React web development. Only when it comes time to compile into an iOS binary and test hardware services will you need a Mac computer. Android fortunately does not have this limitation.

Let’s setup Capacitor now. First we start by enabling Capacitor in our existing application. Type

ionic integrations enable Capacitor

When it’s complete you should see something like this.

Now that we’ve enabled Capacitor in our app we’ll need to add the iOS integration. But before we do that make sure you’ve built your app at least once since Capacitor needs the build folder to exist in order to setup properly. Now type

ionic cap add ios

If you get asked to install the Capacitor cli, enter yes as you’ll need it to run commands. Once this setup completes we can open our iOS app in XCode. Type

ionic cap open ios

Once your XCode project opens you will need to attach your iPhone via usb and then select it in XCode as the device to use for testing (it is also possible to use their emulators, but final testing should always be done on a real device). Now once all this is done and you try and run your project from XCode you will see this error.

This error is indicating Apple’s requirement to associate a developer account with an application. Go back to your file explorer view on XCode and click on the root App icon. You should see this

As you can see on the darker right side of the screenshot, there are multiple tabs and you end up on the General tab first. Click on the Signing & Capabilities tab and you should see this.

On this tab you should see the Team drop down. Select it and choose your desired dev account. Obviously if you don’t have one you’ll have to create it. Once you select the proper team account it will automatically update your Signing Certificate and you’re ready to start your app!

Make sure your iPhone is connected and selected within XCode as the test device. Then click the play button on the upper left corner of XCode. You should see the app auto start up on your iPhone. It will look just like the web app that we started earlier in the web browser.


Great we built and started our first iOS mobile application using Ionic. This was quite a bit of reading and configuration so I’ll end Part I here. In the next post I’ll show how to update the tab navigation and start modifying the components to create our photo app screen.

Part II has been released.

As always if you like helping other devs try DzHaven.com

Introduction to the Apollo GraphQL React Hooks Library

A quick start guide to migrate from react-apollo-hooks to the official React hooks library provided by Apollo

This post is an introduction to @apollo/react-hooks. It is not intended to be exhaustive. However, since the syntax is quite similar to react-apollo-hooks, it should be enough to get you started.

Getting Started

I have a react hooks app called DzHaven that I built entirely using React Hooks, no classes and no HOC. However, at the time @apollo/react-hooks was not available, so I used Daniel Trojanowski’s excellent react-apollo-hooks library for all my client-side code.

It works great, but one thing that always bothered me was the fact that the useQuery calls would run at declaration — instead of the exact moment I wanted. So as I go through my experience I’ll show you how to avoid unnecessary execution of queries, as well as some other useful tidbits.

If you are moving from react-apollo-hooks, the first thing you should know is that you do not need to do the migration in one shot.

react-apollo-hooks and @apollo/react-hooks can live together in the same client app. I did my migration running both providers and I did not see any issues. I’ve included my index.tsx file below with all of my imports, and as you can see, both providers are there and work without issue.

The next thing you need to know is which packages are required for your particular setup. If you plan on supporting only hook functions and will not use the old class-style components and HOC, you can install just the @apollo/react-hooks package.

However, If you need to support all three, you’ll have to install the full react-apollo package. Or if you need either the HOC or older style React components you’ll need to install @apollo/react-hoc or @apollo/react-components respectively.

You should note that if you use only the @apollo/react-hooks as opposed to the full react-apollo package, your bundle size will drop to just 5.1kb from 10.6kb.

So then if you have the bare bones hooks setup, your npm install command would be like below. As shown in the previous code sample, apollo-boost provides the ApolloClient, the InMemoryCache, and some other items to help set up the React client and connect to your Apollo server. I would compare it roughly to using create-react-app in terms of it being a pre-packaged library providing most of what you need to set up, without extra effort.

npm install @apollo/react-hooks apollo-boost

Some Examples

Let’s start with queries. The Apollo library contains the same useQuery hook as the react-apollo-hooks library. They’re called identically so I won’t go over that here. However, there is a new hook called useLazyQuery, which allows delayed execution of your query. Let’s take a look at the code below.

Starting from the top we can see that the output of the query is a bit different. The function execGetThreadData is defined first and this is the method used to call your query when you desire to do so. The name given is of course up to you, but I like prefixing each query caller with “exec”.

After that, you can see multiple properties similar to what existed before, but with the addition of the called property. Basically this property prevents unwanted calls from being accidentally made.

Now if we look at useEffect, starting on line 6, we can see that it looks quite familiar to what we would have done before. However starting on line 11, we can see that we check the called property to make sure that a call on this query was not done yet and then make the call execGetThreadData. This is a minor difference but something to be aware of. Continuing on, I will say that most scenarios will not include the code starting on line 16. However, I wanted to show that refetching data is still possible using the same syntax when required.

For useMutation, the syntax is a bit different. As shown below:

Starting from the top again you can see a function property called execPostThread. Later on inside of useEffect I call this function to execute a mutation. On line 2 I wanted to show that refetchQueries is still supported with the same syntax as react-apollo-hooks. Starting on line 13, I am using the older promises style syntax because the call is from within a useEffect, which doesn’t allow awaiting. However if one is able to await the call then the syntax looks familiar again.

Obviously there’s much more than these features in the library but I hope this quick introduction will show you that getting started with @apollo/react-hooks should not be a massive rewrite of your code if you’ve been using hooks already. Having said that, testing has changed somewhat from the way I was doing it with react-apollo-hooks. Let me know if you would like to see a write up on that topic. Hope this helps.

If you’re a typical dev you probably write code for multiple platforms. If you want to use GraphQL on SwiftUI try this article.

As always if you like helping other devs try DzHaven.com

Sharing Code In a Large JavaScript Project

Trials and tribulations sharing code in JavaScript

I built a fairly large app, called DzHaven, using Node and React, and I found myself with the issue that a lot of JavaScript developers find themselves in — difficulty reusing code. I have a fair amount of experience using C#, and it’s pretty easy to create a module through a separate project and then reference that assembly for code reuse. Obviously you can’t do that with JavaScript. Let’s see what is possible.

Attempt 1: Shared Local Project Folder

This seemed like a no brainer. Just create a folder where anything that can be shared among separate projects can live. Then I’ll just use a relative path to that folder from each project and that should do it.

I think you already know this did not work. All projects these days, Node or otherwise, are going to be NPM packages. These packages have a folder structures and configuration files expecting certain paths from within the project itself. It becomes exceedingly difficult and arduous to deal with relative sub-paths from each project. Something like this gets unwieldy very quickly.

{someroot}/../../../{some sub project}

Additionally a larger project can have multiple NPM projects to deal with, where each project has its own set of unique dependency versions that may not be compatible with each other. So whether it’s language differences between JavaScript versions or NPM package versions, things may not play nicely with all projects.

On top of all this, if the folder is moved or renamed, then everything of course stops working.

Attempt 2: Shared NPM Package

Oh I know I’ll just get a subscription to NPM and create private Node packages. The nice thing about this method is that you get a shared location to store your packages and have some control over versioning too.

Unfortunately, this does not work for the same reason as the first attempt. Since this is also an NPM package, the versions of certain NPM packages inside may conflict with other project dependencies. Additionally, there is the added annoyance of having to compile the project after any changes.

Advertisements

Attempt 3: Locally Shared Git Project Folders

Let me try and explain what this is. Most devs place their code into Git as a code repository for their project. Well, it’s also possible to create a shared folder as a repository in Git and clone it anywhere locally.

This means you could clone the shared project into the src folder of any parent project that may need its code. And using the .gitignore file, one could have this folder ignored by the parent project — so that it is not checked in when the parent is. Now if you have two projects you could clone your shared project as a subfolder locally into each project’s src folder. Something like this,

Project A/src/common and Project B/src/common.

Since the folder common is not an NPM package, you are free to use any package versions you like, as long as you’ve not placed any version-specific code into the folder (but that would defeat the purpose of using a shared code location now wouldn’t it). You are also free to use different versions of Typescript, which is what I’m doing. Also, since the folder exists inside of the parent project’s src folder there is no relative path hell to deal with.

Additionally, if a change is required for the common folder, that change can be made, pushed to Git, and then the other project can pull the change down. And vice versa. Or you can wait to pull on one of the projects until it has updated its own dependencies and can accept the changes.

This method is not perfect, as I cannot use projects that depend on NPM packages. But for my project, DzHaven, it’s a small problem that has not caused any major headaches. Hope this helps!

Advertisements

This site runs on wordpress

%d bloggers like this: