The Anatomy of an Ionic Cordova App

The WhatsNearMe App

The goal:
intelligent search and note taking app

The challenge:

integrate GeoLocation and Google Search API with Google NLP Services and Speech Recognition and make it all work from the same code base on both IOS and Android

The structure
the code structure of the application revolves around singleton service methods that perform all the heavy lifting. the controllers are therefore lighter. Thus services like audio, speech, database handling are invoked and each maintained in proper state. For example, the audio service being a singleton, handles playing, pausing, scrubbing and changing tracks no matter where the user in in the application and tracks the current state such as how many seconds have played etc.

The screen shot above shows the base structure of the files as well as a view into the methods in the factory that access all the apis.

So in more detail:

in the controller

$scope.getNews = function(city, start) {
    if (!$scope.$$phase)  $scope.$apply();


    piano.getNews(city, start).then(function(response) {
            $scope.files = response.data.items;

        },
        function(error) {
            $rootScope.toggle('Oops there was an error getting news.', 2000);
        }
    );
    piano.getNews(city, start, true).then(function(response) {
            $scope.filesimages = response.data.items;
        },
        function(error) {
            $rootScope.toggle('Oops there was an error getting images.', 2000);
        }
    );

}

and in the factory

piano.getNews = function(city, start, image){
    if (image) type = "searchType=image"; else type="1=1";
    var url = "https://www.googleapis.com/customsearch/v1?" + type + "&key=xxxxxxxxxxxxxxxxxxxxx&cx=xxxxxx:xxxxxxx&dateRestrict=d[10]&sort=date&sort=distance&start=" + start +"&q=" + city ;
    data =  $http.get(url);
    return data;
}

thus with such a structure, switching to a cache is simple as the controller does not really care where the scope is populated from

The next challenge was the SQL database. I went with sqllite as it seemed the closest to mysql and then someday when I have to develop a more complex application, the base classes developed for this
would be extended to accomodate more complex interactions and synchronization between locl and remote databases thru webservices.

here is the base DB class

 

angular.module('starter.dbservices', ['starter.config'])

    .factory('DB', function($q, DB_CONFIG) {
        var self = this;
        self.db = null;

        self.init = function() {

            try {
                self.db = window.openDatabase(DB_CONFIG.name, '1.0', 'database', -1);
            } catch(e) {
                console.log(e.message);
            }

            angular.forEach(DB_CONFIG.tables, function(table) {
                var columns = [];

                angular.forEach(table.columns, function(column) {
                    columns.push(column.name + ' ' + column.type);
                });

                var query = 'CREATE TABLE IF NOT EXISTS ' + table.name + ' (' + columns.join(',') + ')';
                self.query(query);
                console.log('Table ' + table.name + ' initialized');
            });
        };

        self.query = function(query, bindings) {
            bindings = typeof bindings !== 'undefined' ? bindings : [];
            var deferred = $q.defer();

            try {
                self.db.transaction(function (transaction) {
                    transaction.executeSql(query, bindings, function (transaction, result) {
                        deferred.resolve(result);
                    }, function (transaction, error) {
                        deferred.reject(error);
                    });
                });
            } catch(e) {
                console.log(e.message);
            }

            return deferred.promise;
        };

        self.fetchAll = function(result) {
            var output = [];

            for (var i = 0; i < result.rows.length; i++) {
                output.push(result.rows.item(i));
            }

            return output;
        };

        self.fetch = function(result) {
            return result.rows.item(0);
        };

        return self;
    })

    // locations table service
    .factory('Locations', function(DB) {
        var self = this;

        self.all = function() {
            return DB.query('SELECT * FROM locations')
                .then(function(result){
                    return DB.fetchAll(result);
                });
        };

        self.getById = function(id) {
            return DB.query('SELECT * FROM locations WHERE id = ?', [id])
                .then(function(result){
                    return DB.fetch(result);
                });
        };

        self.getByLatLong = function(lat,lon) {
            return DB.query('SELECT * FROM locations WHERE latitude = ? and  longitude = ?', [lat,lon])
                .then(function(result){
                    return DB.fetch(result);
                });
        };
        return self;
    })

Leave a Reply

Your email address will not be published. Required fields are marked *