Error googlemaps when try add platform iOS

My project run fine on browser, but when I try add platform iOS, I get this error (code below)

My project run fine on browser, but when I try add platform iOS, I get this error (code below)

Apparently the problem is “cordova-plugin-googlemaps”

I have removed the platform. Then added CocoaPods ($ “sudo gem install cocoapods”), then add:

export LANG=en_US.UTF-8

but I still getting the error.

What is wrong? Can you help me?

Thanks.

--[cordova-plugin-googlemaps]------------------------
From version 2.4.5, the cordova-plugin-googlemaps uses CocoaPod.
No longer necessary com.googlemaps.ios plugin.
Automatic uninstalling com.googlemaps.ios plugin...

Failed to install 'cordova-plugin-googlemaps': undefined
CordovaError: Promise rejected with non-error: '/Library/Ruby/Gems/2.3.0/gems/claide-1.0.2/lib/claide/command.rb:439:in help!\':\u001b[31m[!] You cannot run CocoaPods as root.\u001b[39m (CLAide::Help)\n\n\u001b[4mUsage:\u001b[24m\n\n $ \u001b[32mpod\u001b[39m \u001b[32mCOMMAND\u001b[39m\n\n CocoaPods, the Cocoa library package manager.\n\n\u001b[4mCommands:\u001b[24m\n\n \u001b[32m+ cache\u001b[39m Manipulate the CocoaPods cache\n \u001b[32m+ env\u001b[39m Display pod environment\n \u001b[32m+ init\u001b[39m Generate a Podfile for the current directory\n \u001b[32m+ install\u001b[39m Install projectdependencies according to versions from a\n Podfile.lock\n \u001b[32m+ ipc\u001b[39m Inter-process communication\n \u001b[32m+ lib\u001b[39m Develop pods\n \u001b[32m+ list\u001b[39m List pods\n \u001b[32m+ outdated\u001b[39m Show outdated project dependencies\n \u001b[32m+ repo\u001b[39m Manage spec-repositories\n \u001b[32m+ setup\u001b[39m Setup the CocoaPods environment\n \u001b[32m+ spec\u001b[39m Manage pod specs\n \u001b[32m+ update\u001b[39m Update outdated project dependencies and create new Podfile.lock\n\n\u001b[4mOptions:\u001b[24m\n\n \u001b[34m--silent\u001b[39m Show nothing\n \u001b[34m--version\u001b[39m Show the version of the tool\n \u001b[34m--verbose\u001b[39m Show more debugging information\n \u001b[34m--no-ansi\u001b[39m Show output without ANSI codes\n \u001b[34m--help\u001b[39m Show help banner of specified command\n\tfrom /Library/Ruby/Gems/2.3.0/gems/cocoapods-1.5.3/lib/cocoapods/command.rb:47:in run'\n\tfrom /Library/Ruby/Gems/2.3.0/gems/cocoapods-1.5.3/bin/pod:55:in &lt;top (required)&gt;\'\n\tfrom /usr/local/bin/pod:22:in load'\n\tfrom /usr/local/bin/pod:22:in `<main>'\n'
at cli.catch.err (/usr/local/lib/node_modules/cordova/bin/cordova:30:15)
at process._tickCallback (internal/process/next_tick.js:68:7)
[ERROR] An error occurred while running subprocess cordova.

    cordova platform add ios --save exited with exit code 1.

    Re-running this command with the --verbose flag may provide more information.


React Apps with the Google Maps API and google-maps-react

React Apps with the Google Maps API and google-maps-react

This tutorial aims at integrating the google maps API to your React components and enabling you to display maps on your website.

This tutorial aims at integrating the google maps API to your React components and enabling you to display maps on your website.

Introduction to Google Maps API.

At some point of our lives, we’ve all gotten a chance to use and interact with the google maps, either through finding directions, viewing our current location, ordering cabs or estimating distance and time from one point to another.

Table of Contents

Including the Google maps API in a React App is actually far much easier than expected due to its rich documentation which I highly suggest that you look into, and the npm package by Fullstack React.

Prerequisite:

Go on ahead and grab your API key here.

  1. Simply click on the Get Started button
  2. Tick on the maps checkbox
  3. Click Create A New Project
  4. Once you’ve named your project (whatever you want) you can set up your billing info which will automatically enable your the API and generate the API key.

PS: Do not be hesitant of adding your billing info as the Google cloud platform offers you a 12-month free trial period and will not bill you after the trial period until you give your permission.

Project Setup

For some quick setup, we are going to use facebook’s create-react-app which saves us the hassle of having to configure webpack or babel.

So go on ahead and run this command

npm i -g create-react-app
create-react-app my-googlemap
cd my-googlemap

Before we add any code, let’s go ahead and install our dependency.

npm install --save google-maps-react

Let’s go on and edit our src folder and remove files and imports that we do not need ie

  1. Simply click on the Get Started button
  2. Tick on the maps checkbox
  3. Click Create A New Project
  4. Once you’ve named your project (whatever you want) you can set up your billing info which will automatically enable your the API and generate the API key.

Time to create our component.

We will need to edit our App.js file and instead have our component that will load our google map.

import React, { Component } from 'react';
import { Map, GoogleApiWrapper } from 'google-maps-react';

const mapStyles = {
  width: '100%',
  height: '100%'
};

export class MapContainer extends Component {
  render() {
    return (
      <Map
        google={this.props.google}
        zoom={14}
        style={mapStyles}
        initialCenter={{
         lat: -1.2884,
         lng: 36.8233
        }}
      />
    );
  }
}

export default GoogleApiWrapper({
  apiKey: 'YOUR_GOOGLE_API_KEY_GOES_HERE'
})(MapContainer);

For a simple Google Map, this is literally all the code you need. Go ahead and run your app and ensure that the map loads to the browser.

PS: Do not be hesitant of adding your billing info as the Google cloud platform offers you a 12-month free trial period and will not bill you after the trial period until you give your permission.
The GoogleApiWrapper is simply a Higher Order Component(HOC) that provides wrapper around Google APIs. Alternatively, the GoogleApiWrapper HOC can be configured by passing a function that will be called with the wrapped component’s props and should return the configuration object like so.

export default GoogleApiWrapper(
  (props) => ({
    apiKey: props.apiKey
  }
))(MapContainer)

Markers and infoWindow

Wouldn’t it be exciting to have a Marker and an infoWindow on our map showing our set initialCenter position? So let’s add this functionality to our code.

First we need to import Marker and infoWindow components from the google-maps-react library inorder to help us achieve loading of the two.

import { GoogleApiWrapper, InfoWindow, Marker } from 'google-maps-react';

Notice that our component before was stateless?, We need to add state for state management.

[...]

export class MapContainer extends Component {
   state = {
    showingInfoWindow: false,  //Hides or the shows the infoWindow
    activeMarker: {},          //Shows the active marker upon click
    selectedPlace: {}          //Shows the infoWindow to the selected place upon a marker
  };

Next, we need to add event handlers for when the map and the marker are clicked.

  onMarkerClick = (props, marker, e) =>
    this.setState({
      selectedPlace: props,
      activeMarker: marker,
      showingInfoWindow: true
    });

  onClose = props => {
    if (this.state.showingInfoWindow) {
      this.setState({
        showingInfoWindow: false,
        activeMarker: null
      });
    }
  };

PS: Do not be hesitant of adding your billing info as the Google cloud platform offers you a 12-month free trial period and will not bill you after the trial period until you give your permission.* Prerequisite:

Let’s complete our component by adding our Marker and InfoWindow components to our render method

  render() {
    return (
      <Map
        google={this.props.google}
        zoom={14}
        style={style}
        initialCenter={{ lat: -1.2884, lng: 36.8233 }
      >
        <Marker
          onClick={this.onMarkerClick}
          name={'Kenyatta International Convention Centre'}
        />
        <InfoWindow
          marker={this.state.activeMarker}
          visible={this.state.showingInfoWindow}
          onClose={this.onClose}
        >
          <div>
            <h4>{this.state.selectedPlace.name}</h4>
          </div>
        </InfoWindow>
      </Map>
    );
  }
}

[...]

Run your app and ensure you have the one marker with the infoWindow upon click.

Browser’s current location

Let’s spice things up by having our map pick our browser’s current location. We will be using navigator which is a read-only property that returns a Geolocation object that gives Web content access to the location of the device.

In our src folder create a new file and name it Map.js and create a component named CurrentLocation this is where all our functionality to pick our browser’s location will lie.

We will begin by adding some default props to our CurrentLocation component, since we will need to set the map with a center incase the current location is not provided.This is handled by the boolean prop centerAroundCurrentLocation

import React from 'react';
import ReactDOM from 'react-dom';

const mapStyles = {
  map: {
    position: 'absolute',
    width: '100%',
    height: '100%'
  }
};

export class CurrentLocation extends React.Component {

[...]

}
export default CurrentLocation;

CurrentLocation.defaultProps = {
  zoom: 14,
  initialCenter: {
    lat: -1.2884,
    lng: 36.8233
  },
  centerAroundCurrentLocation: false,
  visible: true
};

Next, we need to make our component stateful,

[...]

export class CurrentLocation extends React.Component {
  constructor(props) {
    super(props);

    const { lat, lng } = this.props.initialCenter;
    this.state = {
      currentLocation: {
        lat: lat,
        lng: lng
      }
    };
  }

[...]

}

Let’s also update our CurrentLocation component to cater for the instance when the Map is first loaded as we cannot solely depend upon the google API being always available, hence we need to check if it’s loaded. And also check if the browser’s current location is provided and recenter the map to it.

[...]

componentDidUpdate(prevProps, prevState) {
    if (prevProps.google !== this.props.google) {
      this.loadMap();
    }
    if (prevState.currentLocation !== this.state.currentLocation) {
      this.recenterMap();
    }
  }

[...]

Let’s define the recenterMap() function which only gets called when the currentLocation in the component’s state is updated and uses the .panTo() method on the google.maps.Map instance to change the center of the map.

[...]

 recenterMap() {
    const map = this.map;
    const current = this.state.currentLocation;

    const google = this.props.google;
    const maps = google.maps;

    if (map) {
      let center = new maps.LatLng(current.lat, current.lng);
      map.panTo(center);
    }
  }

[...]

Next, we need to handle the instance when the map has already loaded.This will be handled by the componentDidMount() Lifecycle method which will set a call back to fetch the current location.

[...]

 componentDidMount() {
    if (this.props.centerAroundCurrentLocation) {
      if (navigator && navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(pos => {
          const coords = pos.coords;
          this.setState({
            currentLocation: {
              lat: coords.latitude,
              lng: coords.longitude
            }
          });
        });
      }
    }
    this.loadMap();
  }

[...]

Notice the loadMap() function? Let’s go on ahead and define it.

[...]

loadMap() {
    if (this.props && this.props.google) {
      // checks if google is available
      const { google } = this.props;
      const maps = google.maps;

      const mapRef = this.refs.map;

      // reference to the actual DOM element
      const node = ReactDOM.findDOMNode(mapRef);

      let { zoom } = this.props;
      const { lat, lng } = this.state.currentLocation;
      const center = new maps.LatLng(lat, lng);
      const mapConfig = Object.assign(
        {},
        {
          center: center,
          zoom: zoom
        }
      );

      // maps.Map() is constructor that instantiates the map
      this.map = new maps.Map(node, mapConfig);
    }
  }

Basically, the loadMap() function is only called after the component has been rendered and grabs a reference to the DOM component to where we want our map to be placed.

Our CurrentLocation component is almost looking up, But we need to ensure that our previous Marker picks our currenct location ie the browsers current location and so we need to introduce Parent-Child concept through the renderChildren() method which will be responsible for actually calling the method on the child component.

Read more about Parent-Child communication here

[...]

 renderChildren() {
    const { children } = this.props;

    if (!children) return;

    return React.Children.map(children, c => {
      if (!c) return;
      return React.cloneElement(c, {
        map: this.map,
        google: this.props.google,
        mapCenter: this.state.currentLocation
      });
    });
  }

[...]

And finally, let’s add our render() method

[...]

render() {
     const style = Object.assign({}, mapStyles.map);
    return (
      <div>
        <div style={style} ref="map">
          Loading map...
        </div>
        {this.renderChildren()}
      </div>
    );
  }

[...]

Lastly, before we wind up we need to update our MapContainer component to include our new changes. So let’s change it to this

import React, { Component } from 'react';
import { GoogleApiWrapper, InfoWindow, Marker } from 'google-maps-react';

import CurrentLocation from './Map';

export class MapContainer extends Component {
  state = {
    showingInfoWindow: false,
    activeMarker: {},
    selectedPlace: {}
  };

  onMarkerClick = (props, marker, e) =>
    this.setState({
      selectedPlace: props,
      activeMarker: marker,
      showingInfoWindow: true
    });

  onClose = props => {
    if (this.state.showingInfoWindow) {
      this.setState({
        showingInfoWindow: false,
        activeMarker: null
      });
    }
  };

  render() {
    return (
      <CurrentLocation
        centerAroundCurrentLocation
        google={this.props.google}
      >
        <Marker onClick={this.onMarkerClick} name={'current location'} />
        <InfoWindow
          marker={this.state.activeMarker}
          visible={this.state.showingInfoWindow}
          onClose={this.onClose}
        >
          <div>
            <h4>{this.state.selectedPlace.name}</h4>
          </div>
        </InfoWindow>
      </CurrentLocation>
    );
  }
}

export default GoogleApiWrapper({
  apiKey: 'YOUR_GOOGLE_API_KEY_GOES_HERE'
})(MapContainer);

Run your app; heading over to our browser, our map should first load with our initialCenter then reload to pick our browser’s current location with the marker positioned to this location and Voilà we are done.

Conclusion

In this article we’ve been able to load our google maps React component, add a marker and have an infoWindow onto it and also have the map pick our current location. While we did not dive into adding more functionalities to our map such as, having polylines and polygons or adding event listeners, I highly recommend that you look into them

Learn More

Full Stack Developers: Everything You Need to Know

React Hooks Tutorial for Beginners: Getting Started With React Hooks

Learn React.js for Beginners

Learn React - React Crash Course 2019 - React Tutorial with Examples

React Router: Add the Power of Navigation

React - The Complete Guide (incl Hooks, React Router, Redux)

Modern React with Redux [2019 Update]

React Native - The Practical Guide

Building an Ionic Geolocation Tracker using Google Maps

Building an Ionic Geolocation Tracker using Google Maps

This Ionic Geolocation Tracker tutorial explains how to build an Ionic Geolocation tracker using Google Maps. How to Build an Ionic Geolocation Tracker with Google Maps and Track Drawing. Building an Ionic Geolocation Tracker using Google Maps

Within your Ionic App you can easily track the position of your users with the Ionic Geolocation plugin. It is also easy to add a Google Map to your app, so why not combine these 2 features into a useful app?

In this tutorial we will build an Ionic Geolocation Tracker which can track the route of users, display the path the user has walked inside a Google map and finally also save those information to display previous runs. It’s gonna be fun!

Starting our Geolocation Tracker

We start by creating a blank new Ionic app and install the Geolocation plugin and also the SQLite storage so Ionic Storage uses a real database inside our final app. Right now we already add a variable to the Geolocation plugin but we need another fix for iOS later on as well:

ionic start devdacticLocationTracker blank
cd devdacticLocationTracker
ionic cordova plugin add cordova-plugin-geolocation --variable GEOLOCATION_USAGE_DESCRIPTION="To track your walks"
ionic cordova plugin add cordova-sqlite-storage
npm install --save @ionic-native/geolocation

As we want to use Google Maps we also need to load the SDK and therefore we can simply add this directly to our src/index.html right before the cordova import:

<script src="http://maps.google.com/maps/api/js?key=YOUR_API_KEY_HERE"></script>

Also, you need to insert your own key here (simply replace YOUR_API_KEY_HERE) but you can easily generate an API key by going to this page.

Now we also need to load our plugins, therefore make sure to import and connect them inside your app/app.module.ts:

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';
 
import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';
 
import { Geolocation } from '@ionic-native/geolocation';
import { IonicStorageModule } from '@ionic/storage';
 
@NgModule({
  declarations: [
    MyApp,
    HomePage
  ],
  imports: [
    BrowserModule,
    IonicStorageModule.forRoot(),
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
    Geolocation
  ]
})
export class AppModule {}

Finally, I already mentioned that we need some fix for iOS. If you build your app you will get a warning that you haven’t added the according information to your *.plist why you want to use the Geolocation, but you can simply add this little snippet to your config.xml which will always add the NSLocationWhenInUseUsageDescription when you build the iOS app:

<edit-config file="*-Info.plist" mode="merge" target="NSLocationWhenInUseUsageDescription">
    <string>This App wants to track your location</string>
</edit-config>
Basic View and Google Map Positions

We’ll split the next part into 2 sections, and we start with the basic view of our Google Map. Our view mainly consists of a button to start and stop our tracking, the actual Google map and also a list of previous routes which can be loaded once again into our map just lake you are used to it from your running apps!

Go ahead and start with the view by changing the pages/home/home.html:

<ion-header>
  <ion-navbar color="primary">
    <ion-title>
      GPS Tracking
    </ion-title>
  </ion-navbar>
</ion-header>
 
<ion-content padding>
 
  <button ion-button full icon-left (click)="startTracking()" *ngIf="!isTracking">
      <ion-icon name="locate"></ion-icon>
      Start Tracking
    </button>
  <button ion-button full color="danger" icon-left (click)="stopTracking()" *ngIf="isTracking">
      <ion-icon name="hand"></ion-icon>
      Stop Tracking
  </button>
 
  <div #map id="map"></div>
 
  <ion-list>
    <ion-list-header>Previous Tracks</ion-list-header>
    <ion-item *ngFor="let route of previousTracks">
      {{ route.finished | date }}, {{ route.path.length }} Waypoints
      <button ion-button clear item-end (click)="showHistoryRoute(route.path)">View Route</button>
    </ion-item>
  </ion-list>
</ion-content>

Nothing fancy yet, the map is really just one element and we’ll do all the logic from inside the class but first we have to add some styling to make the map look good inside our app.

Therefore, add this bit of styling inside your pages/home/home.scss:

page-home {
    #map {
        width: 100%;
        height: 300px;
      }
}

We want to load the map on start of the app and also focus it on our current position so we will notice when the track drawing begins.

First of all we need a reference to our map inside the view using @ViewChild and then we can create a new map using new google.maps.Map with additional options. In this case we disable the different view options displayed on the map and pick the Roadmap style for the map.

We’ll also keep track of this new map variable as we need it later to draw our tracks!

After the map initialisation we also try to get the current position of the user with the geolocation plugin and if we get back the coordinates we can focus our map on this spot! We could also display a marker here but that’s left for the reader.

Go ahead and change your pages/home/home.ts to:

import { Component, ViewChild, ElementRef } from '@angular/core';
import { NavController, Platform } from 'ionic-angular';
import { Geolocation } from '@ionic-native/geolocation';
import { Subscription } from 'rxjs/Subscription';
import { filter } from 'rxjs/operators';
import { Storage } from '@ionic/storage';
 
declare var google;
 
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  @ViewChild('map') mapElement: ElementRef;
  map: any;
  currentMapTrack = null;
 
  isTracking = false;
  trackedRoute = [];
  previousTracks = [];
 
  positionSubscription: Subscription;
 
  constructor(public navCtrl: NavController, private plt: Platform, private geolocation: Geolocation, private storage: Storage) { }
 
  ionViewDidLoad() {
    this.plt.ready().then(() => {
      this.loadHistoricRoutes();
 
      let mapOptions = {
        zoom: 13,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        mapTypeControl: false,
        streetViewControl: false,
        fullscreenControl: false
      }
      this.map = new google.maps.Map(this.mapElement.nativeElement, mapOptions);
 
      this.geolocation.getCurrentPosition().then(pos => {
        let latLng = new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude);
        this.map.setCenter(latLng);
        this.map.setZoom(16);
      }).catch((error) => {
        console.log('Error getting location', error);
      });
    });
  }
 
  loadHistoricRoutes() {
    this.storage.get('routes').then(data => {
      if (data) {
        this.previousTracks = data;
      }
    });
  }
}

You might have noticed that we also added declare var google at the top which prevents TypeScript errors later inside our code.

Right now, our app is also loading historic routes from the storage using loadHistoricRoutes but of course that’s still empty. Later this will be an array of objects, so let’s work on adding data to that array!

Tracking Geolocation and Drawing on Google Map

We now need to do 2 things:

  • Watch the current GPS position of the user
  • Draw a path on our map

After that we also need to add the tracked route to the storage, but that’s the easiest part of our app.

Inside the startTracking function we subscribe to the position of the user which means we automatically get new coordinates from the Observable!

We can then use those values to first of all add a new entry to our trackedRoute array (to have a reference to all the coordinates of the walk) and then call the redrawPath if we want to immediately update our map.

Inside that function we also check a variable currentMapTrack which always holds a reference to the current path on our map!

To actually draw we can use a Polyline which can have a few options and most important the path which expects an array of objects like [{ lat: '', lng: '' }]

This function will then automatically connect the different coordinates and draw the line, and finally after we have constructed this line we call setMap on this line which draws it onto our map!

Now go ahead and add these 2 functions below the previous functions inside pages/home/home.ts:

startTracking() {
    this.isTracking = true;
    this.trackedRoute = [];
 
    this.positionSubscription = this.geolocation.watchPosition()
      .pipe(
        filter((p) => p.coords !== undefined) //Filter Out Errors
      )
      .subscribe(data => {
        setTimeout(() => {
          this.trackedRoute.push({ lat: data.coords.latitude, lng: data.coords.longitude });
          this.redrawPath(this.trackedRoute);
        }, 0);
      });
 
  }
 
  redrawPath(path) {
    if (this.currentMapTrack) {
      this.currentMapTrack.setMap(null);
    }
 
    if (path.length > 1) {
      this.currentMapTrack = new google.maps.Polyline({
        path: path,
        geodesic: true,
        strokeColor: '#ff00ff',
        strokeOpacity: 1.0,
        strokeWeight: 3
      });
      this.currentMapTrack.setMap(this.map);
    }
  }

The last missing function is when we stop the tracking. Then we need to store the information of the previous walk and also clear our map again.

Along with the tracked route we also store the current date to have some sort of information on the actual element, of course a running app would allow to review and share your path and then perhaps add more information like the time it took, distance covered and so on.

For now though this is enough, and the last function of this tutorial is the loading of historic routes – which is actually only a call to our redraw function with the information stored in the object!

Therefore, wrapn this up by adding the last functions to your pages/home/home.ts:

stopTracking() {
  let newRoute = { finished: new Date().getTime(), path: this.trackedRoute };
  this.previousTracks.push(newRoute);
  this.storage.set('routes', this.previousTracks);
 
  this.isTracking = false;
  this.positionSubscription.unsubscribe();
  this.currentMapTrack.setMap(null);
}
 
showHistoryRoute(route) {
  this.redrawPath(route);
}

That’s it, now make sure to run your app on a real device or at least a simulator where you can change the location to test out the functionality like I did within the initial Gif at the top of this tutorial!

You can also find a video version of this article below.

Ionic 4, Angular 7 and Cordova Crop and Upload Image

Ionic 4, Angular 7 and Cordova Crop and Upload Image

The comprehensive step by step tutorial on crop, and upload Image using Ionic 4, Angular 7 and Cordova

The comprehensive step by step tutorial on crop, and upload Image using Ionic 4, Angular 7 and Cordova. We will use Native Ionic Cordova Crop, File Transfer plugins and it’s dependencies. In this tutorial, we will use our existing uploader API that you can find on our GitHub which it uses Node.js, Express.js, and Multer. Or, you can use your own backend or API that using HTML Form Encoding multipart/form-data. The scenario is in the Ionic 4 App, click the Camera button inside the preview image then it will open image picker. After image picked then it will go to cropping popup that you can crop anyway you like then upload to the API. After the successful upload, the Ionic 4 app will preview the Image by URL that saved to the API server.

Table of Contents:

The following tools, frameworks, and modules are required for this tutorial:

Before going to the main steps, we assume that you have to install Node.js. Next, upgrade or install new Ionic 4 CLI by open the terminal or Node command line then type this command.

sudo npm install -g ionic

You will get the latest Ionic CLI in your terminal or command line. Check the version by type this command.

ionic --version
4.12.0

1. Create a New Ionic 4, Angular 7 and Cordova App

To create a new Ionic 4 App, type this command in your terminal.

ionic start ionic4-crop blank --type=angular

If you see this question, just type N for because we will installing or adding Cordova later.

Install the free Ionic Appflow SDK and connect your app? (Y/n) N

Next, go to the newly created app folder.

cd ./ionic4-crop

As usual, run the Ionic 4 App for the first time, but before run as lab mode, type this command to install @ionic/lab.

npm install --save-dev @ionic/lab
ionic serve -l

Now, open the browser and you will the Ionic 4 App with the iOS, Android, or Windows view. If you see a normal Ionic 4 blank application, that’s mean you ready to go to the next steps.

2. Install and Configure Image Crop, File Transfer Plugins, and Dependencies

We will install all required plugins for this tutorial. First, we have to install Native Cordova plugins and Ionic 4 Angular 7 Modules by running these commands.

ionic cordova plugin add cordova-plugin-crop
npm install @ionic-native/crop
ionic cordova plugin add cordova-plugin-camera
npm install @ionic-native/camera
ionic cordova plugin add cordova-plugin-file-transfer
npm install @ionic-native/file-transfer
ionic cordova plugin add cordova-plugin-file
npm install @ionic-native/file

Next, open and edit src/app/app.module.ts then add these imports.

import { ImagePicker } from '@ionic-native/image-picker/ngx';

Add that import to @NgModule Providers.

providers: [
&nbsp; StatusBar,
&nbsp; SplashScreen,
&nbsp; { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
&nbsp; ImagePicker
],

3. Implementing Image Crop and File Upload/Transfer

We will be using the existing Home component or page to implementing Image Preview, Picker, Crop and Upload. For that, open and edit src/app/home/home.page.html then replace all HTML tags with these.

<ion-header>
&nbsp; <ion-toolbar>
&nbsp; &nbsp; <ion-title>
&nbsp; &nbsp; &nbsp; Ionic 4 Crop Upload
&nbsp; &nbsp; </ion-title>
&nbsp; </ion-toolbar>
</ion-header>

<ion-content padding>
&nbsp; <ion-card>
&nbsp; &nbsp; <img *ngIf="!fileUrl" src="assets/no-image.jpeg"/>
&nbsp; &nbsp; <img *ngIf="fileUrl" src="{{fileUrl}}"/>
&nbsp; &nbsp; <ion-card-content>
&nbsp; &nbsp; &nbsp; <ion-button color="medium" size="large" (click)="cropUpload()">
&nbsp; &nbsp; &nbsp; &nbsp; <ion-icon slot="icon-only" name="camera"></ion-icon>
&nbsp; &nbsp; &nbsp; </ion-button>
&nbsp; &nbsp; </ion-card-content>
&nbsp; </ion-card>
</ion-content>

Next, open and edit src/app/home/home.page.ts then add these imports.

import { Crop } from '@ionic-native/crop/ngx';
import { ImagePicker } from '@ionic-native/image-picker/ngx';
import { FileTransfer, FileUploadOptions, FileTransferObject } from '@ionic-native/file-transfer/ngx';

Inject those imports to the constructor.

constructor(private imagePicker: ImagePicker,
&nbsp; private crop: Crop,
&nbsp; private transfer: FileTransfer) { }

Add the variables for hold image URL and response data.

fileUrl: any = null;
respData: any;

Create a function for crop and upload an image file to the API server.

cropUpload() {
&nbsp; this.imagePicker.getPictures({ maximumImagesCount: 1, outputType: 0 }).then((results) => {
&nbsp; &nbsp; for (let i = 0; i < results.length; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; console.log('Image URI: ' + results[i]);
&nbsp; &nbsp; &nbsp; &nbsp; this.crop.crop(results[i], { quality: 100 })
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .then(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; newImage => {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log('new image path is: ' + newImage);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const fileTransfer: FileTransferObject = this.transfer.create();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const uploadOpts: FileUploadOptions = {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fileKey: 'file',
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fileName: newImage.substr(newImage.lastIndexOf('/') + 1)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; };

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fileTransfer.upload(newImage, 'http://192.168.0.7:3000/api/upload', uploadOpts)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.then((data) => {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;console.log(data);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;this.respData = JSON.parse(data.response);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;console.log(this.respData);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;this.fileUrl = this.respData.fileUrl;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}, (err) => {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;console.log(err);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;});
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; error => console.error('Error cropping image', error)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; );
&nbsp; &nbsp; }
&nbsp; }, (err) => { console.log(err); });
}

As you can see, we use the IP address to access Express.js API from the device. The uploaded image file accessible from the device through [http://192.168.0.7:3000/images/filename](http://192.168.0.7:3000/images/filename "http://192.168.0.7:3000/images/filename") URL.

4. Run and Test Ionic 4, Angular 7 and Cordova App on iOS/Android Devices

We assume that you have cloned the Node.js, Express.js and Multer image uploader here https://github.com/didinj/node-express-image-uploader.git. Next, open a new Terminal or cmd-tab then go to the cloned Express image uploader.

npm install
nodemon

Next, to run on Android devices type this command while the device connected.

ionic cordova platform add android
ionic cordova run android

To run on iOS simulator or device, we have to build it first.

ionic cordova platform add ios
ionic cordova build ios

Then open and run the iOS app from the XCode. You will this view from your Android device or iOS simulator.

That it’s, the Ionic 4, Angular 7 and Cordova Crop and Upload Image tutorial. You can get the full source code from out GitHub.