Enterprise in House Mobile Applications
Among the facilities for an in House development, we can choose among full native implementations using Objective C and Xcode , a cross code generator approach using for example Flash Builder Mobile or a combination of both.
This article shows how we can implement the same functionality (a simple geocoordinates recording system) using both Flash Builder and Xcode (Objective C) as client Apps sharing the same backend infrastructure written in Java and running under a J2EE compliant application server.
The aim is not only to present a direct comparison using both client development environment but also to provide some indications of typical side effects when a cross code generation approach is selected. Aspects that in the context of an in house mobile projects are important to evaluate and consider.
Enterprise in House Mobile Applications
Comparing Flash Builder Mobile and Xcode Implementations by Example
Author: Fabio Trentini, BTC Business Technology Consulting, November 2011
Mobile technologies applied to access core business data and services belongs today to every modern organization. The required development infrastructure and tools are constantly enhanced and these improvements enables more and more companies with the option of also considering in house tailored solutions using internal skills and resources.
We may recall a well knownold question in the IT world (still valid today for the subject covered in this document) : "Shall we buy or shall we make ?" The experience shows that in some situation a buy approach clearly resulted in relevant benefits. There are on the other hands situations where companies are still today "customizing" what they were supposed to buy as a whole (out of the box) some years ago.
The main concern is therefore to identify whether the development strategy should go towards outsourcing, buying a standard solution to be customized or consider an in house development. This document will rather focus on aspects more related to the latter approach.
The aim here is to describe under the same hood 2 parallel development approaches (i.e. a cross code generation and a full native implementation) both used to solve the same functionality in order to allow a better comparison and discuss ,with concrete examples, differences and similarities focusing on where to put our attention for the evaluation of an enterprise mobile development environment.
The following diagrams summarizes the differences between a Native and a Cross generation development environment and at the same time the subjects covered by this paper.

On one side we will show where Flash Builder can greatly simplify the implementation of Apps by cross generation and on the other side, we will see in which potential situations, the implementation approach may go towards a native solution.
Development environment strategy aspects
I will emphasize more than once, independently from the side effects described in some specific situations, and to avoid any misunderstanding since the beginning, that both development environments (Adobe Flash Builder and Apple Xcode) are more than valid options for the development of in house corporate mobile applications (or targeting the App Store as this is already the case of course for both technologies)
Since the choice of going towards the one or the other approach is more related to the specific projects requirements, the intention here will be to focus in particular on some elements of decision to be applied in the context of an evaluation process rather than on the specific technical details related to the selected case study. In other words, the sample case study will tend towards a native implementation (or a cross generation with native extensions) at the end but this does not make it a generic rule for every situation. As also shown on my other article on the subject, there are also situations where Flash Builder would easily allow to integrate corporate data and services in IOS Apps with very reduced efforts.
Time to market plays also a major role in the final decision. Although cross generators are constantly improving from release to release, Apple with Xcode will always bring the latest iPad/Iphone innovations supported with the related implementation frameworks at first. In this respect, cross generators will always need some time to fill in the gap. One way to fill the gap may be native extension that will be also shortly discussed in this paper.
Both Flash Builder and Xcode are installed on my Workstations and Notebook and I follow in parallel both environments. In this respect, even if a cross generation approach would result as being more appropriate for in house development within a given company, it is still important, and mostly recommended, to always keep an eye open (with the related internal knowhow) on the native counterpart and this is what we are going to cover with concrete code examples.
Terminology
Before getting into more details on the subject, I start with the some terminology required in order to follow the explanations (more than obvious for developers but maybe not necessarily known to less technical oriented readers).
Native languages are the programming languages supported for development on a specific device (platform), On an iPad or iPhone for example, we use Objective C (that may also be combined/exended with C++ or C) in order to write IOS native Apps. The "IOS native applications" referred in this document, have therefore to be understood as being Mobile Applications that are fully written in Objecitive C and Xcode is the Apple development environment used for this purpose.
A cross code generator (or cross code compilers) is used in a development process where a programmer can use a familiar language (such as Actionscript or MMXML as far as Flash Builder is concerned) in order to generate native code that will in turn be compiled and installed on a target device. In other words, a Flash Builder mobile developer can write a program with Actionscript and deliver a native application without writing a single line of Objective C. A cross code generator has the advantage to target more than a single environment i.e. produce code in different native languages and thus enable the resulting applications to run on different devices. An application written with Flash Builder may therefore not only run under IOS, but may also be used on Android or Blackberry devices.
This sounds perfect and it is effectively in some cases extremely powerful although we need to be aware that there are also as we will see, in some specific cases, side effects that need to be considered. In order to provide a "generic" cross compiler solution, vendors need to consider in fact common denominators among target environments i.e focus on what can be safely generated and run on every supported physical devices. The limitations of a cross generation solution is therefore not necessarily due to the fact that products are not well designed but simply because, in order to cover different platforms and devices, compromises are inevitably required.
We may therefore miss a feature for IOS in Flash Builder (and we concretely will experience this in our example) not because Adobe did not make an excellent job, but simply because some specific required API's are not available on all the target devices leaving the vendor in the situation to discard these from the generation process.
One way to cope with missing API´s is the usage of so called Native Extension Calls i.e. the facility to include pieces of native code in order to access resources specific to a given target device. By following this approach however, and assuming that the goal is to cross generate an application for different kind of devices (IOS and Android for example), the programmers will have to implement the extension calls on each target platform and using the related target native programming languages.
Extension calls will become however the best companion for developer focusing on tools such as Flash Builder since the probability to use these "workarounds" omen day or another is relatively high.
An element of decision is also to consider how much of these native calls will be effectively required to fulfill the business requirements . If the number of required native facilities is high, we may seriously cope with the question of whether a cross generation would still make sense (and I repeat myself when I emphasize that this has to be seen on each individual case situation and not as a generic rule).
Cross generation vs. native implementation
As already mentioned, there is no definitive and generic answer to the question of which approach is more appropriate between a cross generation and native implementation and probably, if you tend towards a cross generation, you will probably use a combination of both.
We can find in the App Store quite a number of IOS applications written by cross generation and among others also created with Flash Builder Mobile which implicitly means that the Adobe development platform can also be successfully used for this purpose. We need however to carefully evaluate our specific business requirements since, according to the nature of the application, there may be potential (unwanted) side effects.
In the practical example selected for this paper I explicitly considered a situation where:
- The final mobile solution will be technically possible with both approaches (i.e. as a native application written in Objective C or using cross generation with Flash Builder Mobile)
- The rendering on the internet (see demo component embedded in this document below) is 100% written with Flash Builder where the available server infrastructure is already set up for this platform and in particular with services based on the Adobe AMF protocol.
- The final solution with cross generation will be bound with compromises or workarounds to be evaluated and agreed with the users.
- In order to really fulfill the full set of user requirements, we will have, to date at least, to opt for a native implementation with Objective C.
- The native implementation will be easy to implement under Xcode. The efforts required looking for workarounds in Flash Builder would be, in our case at least, roughly equivalent as writing the full application with native code.
I therefore selected a use case situation where the pre-requisites would be initially in favor of Flash Builder (since already in house for intranet and internet components) but once we will start with the implementation, we will realize that some missing functionality in the IOS cross generation, required by our users, will bring us to re-consider our decision and opt for Xcode (and since we will have by the end of this article some know how in both technologies, this won't be a major hurdle)
The use case will also show us that the switch from Flash Builder Mobile to Objective C will not require changes on the backend java based services since these, although originally build for the Flash Builder internet solution, and using the AMF protocol, will be fully re-used, without any changes, within the Objective C implementation.
This emphasize an important aspects. The backend infrastructure remains often unaffected and the efforts on the client remains therefore comparable for both client implementations.
To avoid any misunderstanding at this point, a short note is necessary:
I worked quite a lot with Flex/Flash Builder for intranet and internet applications and I love this platform as much as Xcode, I started writing IOS applications with Objective C (thus native Approach) since the Adobe Mobile cross generation facilities were not yet available since the beginning.
In this respect, I would definitively consider and evaluate Flash Builder for the cross generation of mobile solutions but not necessarily for any situation and as a default. By mentioning some drawbacks (in the selected case study mostly related, to date at least, to missing IOS API's in Flash Builder) the aim is by far not to discredit the Adobe solution but rather to give an idea of the nature of potential situations that can be encountered.
Case study introduction
The case study will be based on a classical deferred update approach where both the Objective C and Flash Builder Apps will keep realtime recorded geolocation information on a local database (i.e. an SQLLite database on the iPad or iPhone) and issue remote server requests (updates) in order to upload and store the data on a remote central database (used by the internet Flash Builder component to render the information on the internet using the running demo component below).
A deferred server update approach becomes necessary in situations where we can expect that a remote service may not always be reachable or simply to reduce the number of server requests .
The approach is summarized in the following diagram:

All we want to implement is a therefore rudimentary system allowing to follow on the internet the path of a user based on GPS coordinates recorded on his/her IOS mobile device.
The example below shows, using the Google MAP 3D API's, a resulting sequence of geo coordinates recorded on an iPad during a trip in Zurich. The coordinates have been recorded in a remote database using the code described in this document. The applications has been tested on an Ipad and on an Iphone 4S using both the Flash Builder and the Objective C native implementation.
From the internet user perspective (you and me), there are not differences between what was recorded using the cross generated Flash Builder App or the Xcode native implementation.
The Google-Map 3D Api's used to render the data are actually (and unfortunately) not available for cross generation in the Flash Builder Mobile implementation thus we can view the 3D results only on an internet Browser equipped with the flash player.
Development environment evaluation
As already mentioned, the development environments currently available on the market, as far as IOS is concerned, imply to evaluate (and choose) between a cross generation solution (such as Flash Builder Mobile) and a native implementation using Xcode.
Assuming the business objectives as known and clearly defined (in short: we know what we want to bring on a mobile device - a sequence of geocoordinates per user in our case), the technical evaluation process should focus in the collection of some preliminary information such as (among others):
- What kind of devices will our users need in their daily business ? IOS ? Android ? both ?
- What kind of internal resources/skills do we have and in which area?
- Do we need to outsource the implementation or can we train our IT staff to implement the final solution ? At which costs?
- Do we plan other internal mobile applications in the further ?
- Can we cross generate part of existing components ?
- How are the cross generated facilities supported on each target device ? Do these really work as expected ?
- Are we expecting native extensions ? If yes how many and in which area ?
- What is the performance of the cross generated applications on the target devices compared with a native implementation ? can we cope with it ?
- What can we re-use from the existing business layer and infrastructure ?
This drive us to the comparison analysis.
A proof of concepts in form of a small prototype, focusing on the project main key functionalities is at this point in time recommended for companies using a cross generation solution for the first time and in order to effectively assess if the approach really can match the expectations.
In our evaluation process we need to compare the business requirements with the available facilities and according to the actual state of the development environments. The use case is focusing on out of the box functionalities (i.e. we assume first that no Native Extensions should be used) currently provided by Adobe Flash Builder Mobile (version 4.5) and XCode (Ver 4.2) and as per november 2011. I emphasize the date and period since some of the "missing" facilities such as a working network status detection under IOS (this is only possible, to date, under IOS via native extensions) may be added in future releases.
I therefore selected a case study based on geolocations and GoogleMaps in order not only to show and compare the coding aspects but also and in particular because the example will demonstrate that the implementation will be, according to the expectations :
- … much more easier in Flash Builder if we accept some compromises. (i.e. no realtime rendering of the patron the physical device)
- … much more effective in Xcode and Objective C if we need to fulfill the full set of user requirements.
In order to present this aspects, I will use in this article 3 technologies:
- Flash Builder for the mobile implementation of the first Application using Adobe Actionscript/MMXML and by cross generation targeting IOS.
- Flash Builder for the rendering on the internet of both data collected with the XCode and Flash Builder mobile implementations (i.e. the GoogleMap3D component shown above)
- Objective C for the native implementation of the second Mobile APplication using XCode.
- Java for the common server side implementation (running under Tomcat or Glassfish and using BlazeDS, Spring and Hibernate)
Both applications need to rely on the backend infrastructure and data contents. We can reuse the backend service in Objective C even if these are originally written with an Adobe specific protocol (such as AMF) and without any changes on the server.
Short Feasibility and evaluation analysis of the GoogleMap functionalities
Nothing against other geolocation solutions to frameworks of course but we need, for the sake of explanations, components (and user requirements) that, to date at least, would be directly available, out of the box, only using XCode.
The GoogleMap (mapView) case is in our scope, to date at least, interesting since we have the following situation.
- There is no IOS direct mapping and cross generation for mapView (Apple MapKit framework) in Flash Builder Mobile at the moment
- Developers under Flash Builder may opt for a solution consisting in calling the GoogleMap component via the local Browser (even if viable, miles away from the flexibility provided by the MapKit API's of Apple). This would however be a potential workaround for our users.
- We could opt for other geolocation frameworks supported in Flash Builder under IOS. The users are however located ( of course again for our case study assumptions) in a region that is covered only by GooleMap with the required level of details.
- We can consider a full native implementation approach since the coding efforts in Objective C are for this case moderate (we will actually describe with code examples this solution as well)
- Last but not least, we can wait for the real time rendering on the mobile device and implement a "temporary" solution where IOS users will not get any real time rendering until this will be viable and available in Flash Builder. Notice here the following: we are in November 2011. You may want to check, if you read this article at a later stage, if a solution in this particular case is now available.
By the time this article was written, Adobe release Flash Builder 4.6 where , among other improvements, the implementation of Native Extension has been integrated and simplified for the developer. I will cover this aspects in a separate article. For the moment, assume therefore no workarounds in form of Native Extensions.
We need therefore now to choose among one of the following options:
- Convince our user to stay with a mobile solution without a real time rendering on a Map (thus use Flash Builder)
- Convince our users that we may use another solution than GoogleMap. As we said, this is in our case not acceptable
- Convince our user to use Android instead of IOS (under Android we could use a "component counterpart" for GoogleMap)
- Try to solve the problem by writing a native external call extension (we would still need in this case Objective C skills to achieve this)
- Wait until Adobe will fully support the GoogleMap component and add it at a later stage
- Write the Client Application in Xcode ( to date and in this particular case, actually the most effective solution in terms of efforts)
All I did was therefore to select what we need in terms of functionalities and make a first rough evaluation of what would be be the side effects and requirements if I would opt for Flash Builder. We would? need to implement all of this in a prototype of course, but at least check on the internet if the one or the other functionalities may present potential known issues. In the next paragraph I will report an example that I also personally discovered at runtime but for which we can find feedback on the internet as well.
A particular case: The IOS network detection case in Flash Builder:
I mention here this particular feature, since this shows typical potential side effects on the cross generation approach. Again, the aim is not to discredit Flash Builder of course, but simply to emphasize the importance of a pre-evaluation of key functionalities.
Since we opt for a deferred update, we may want to consider, as this is often the case in such situations, to enable a remote server update when the network (internet access) is available and maybe extend this functionality by enabling an update if and only if the user is located in a WIFI area.
We can find a very simple code example to achieve this in Actionscript (which is more elegant from the coding point of view than the counterpart in Objective C by the way) but the issue here is that the API's do not work under IOS (i.e the API delivers an empty result on the device). According to the feedback on the internet, as mentioned for example in this post, the API's for network detection works however for Android and Blackberry.
This particular case does not make a generic rule for all the API's of course but in the project planing, consider a contingency for clarifying situations like this one.
It has to be emphasized that for this particular case, we find in the internet a native call extension example where we can invoke (recommended under Flash Builder 4.6) the IOS native network detection API's.
Important preliminary comment of the case study technical implementation
Readers interested in implementing and test the code presented in the following paragraphs must consider the following :
Using the geolocation APIs may issue repetitive internet accesses (triangulation over WLAN access points) for example and therefore may also be bound with additional data transfer costs. If you test the application on a physical device where a flat rate is not applied by your internet mobile provider, this may imply additional communication fees. Running the application abroad for example, or over a long period of time, may also be source of additional communication fees. In this respect, check the data consumption on your device and clarify with your provider what would be the implications of running such kind of applications on a given region. Last but not least, power supply consumption may increase if the GPS is used over a long period of time.
The Flashbuilder Mobile Implementation
Let's have a look first how to implement the GPS path recorder using Fash Builder and Actionscript. As you will realize, the implementation will be more straightforward and compact if compared to Objective C.
Important Note: The code below delivers geo location coordinates only on physical devices i.e. it will not work on a Mobile Simulator. There are workarounds for this in order to enable local GPS debugging. Typically, one way is to detect if we are running the application within the mobile simulator and return, if this is the case, a fake geo location.
I will omit the details related to the SQLLite part of the implementation but simply provide some generic hints. The reader will also have to consider that we describe here the basics i.e. we will not describe here a full implementation. For example, we may want to implement a local path management allowing the user to keep, delete or modify an existing recording path.These additional functionalities would bring us out of scope.
The Flashbuilder Implementation allowing to collect GPS data is the following:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="GEOLOC">
<s:layout>
<s:VerticalLayout gap="5" paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5"/>
</s:layout>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import ch.btconsulting.mobbileutils.dbutils.BTCMobileDBConnector;
import ch.btconsulting.mobbileutils.dbutils.BTCMobileDBUtils;
import flash.sensors.Geolocation;
import mx.collections.ArrayCollection;
private var geo:Geolocation = new Geolocation();
var btcDbConnector:BTCMobileDBConnector=new BTCMobileDBConnector();;
private function updateLocation(geoEvent:GeolocationEvent):void{
btclatitude.text=geoEvent.latitude.toFixed(10);
btcLongitude.text=geoEvent.longitude.toFixed(10);
btcAccuracy.text=geoEvent.horizontalAccuracy.toFixed(10);
btcHeading.text=geoEvent.heading.toFixed(10);
storeLocation(geoEvent);
}
protected function doGPSSTOP(event:MouseEvent):void{
msg.text="Currently not Recording"
btcDbConnector.sessionDBConn.close();
geo.removeEventListener(GeolocationEvent.UPDATE, updateLocation);
}
protected function doGPSRUN(event:MouseEvent):void{
msg.text="RECORDING PATH";
btcDbConnector.btcOpenDb("assets","btcgeomobile.db");
geo.setRequestedUpdateInterval(1000);
geo.addEventListener(GeolocationEvent.UPDATE, updateLocation);
}
protected function storeLocation(geoEvent:GeolocationEvent):void{
var geoItem:Object=new Object();
geoItem.PATHID=btcId.text;
geoItem.LATITUDE=geoEvent.latitude.toFixed(10);
geoItem.LONGITUDE=geoEvent.longitude.toFixed(10);
geoItem.ACCURACY=geoEvent.horizontalAccuracy.toFixed(10);
geoItem.HEADING=geoEvent.heading .toFixed(10);
BTCMobileDBUtils.appendRecord(btcDbConnector,"BTCGEOLOCATIONS",geoItem);
msg.text="Item Stored in the database"
}
]]>
</fx:Script>
<s:Label id="msg" width="662" height="40" fontWeight="bold" text="Currently NOT recording"
textAlign="center" verticalAlign="middle"/>
<s:HGroup width="100%" verticalAlign="middle">
<s:Label width="100" text="Path ID"/>
<s:TextInput id="btcId" width="269" text="TESTPATH" textAlign="left"/>
</s:HGroup>
<s:HGroup width="100%">
<s:Label width="100" text="Heading"/>
<s:Label id="btcHeading" width="269" textAlign="center" verticalAlign="top"/>
</s:HGroup>
<s:HGroup width="100%">
<s:Label width="100" text="Latitude"/>
<s:Label id="btclatitude" width="269" textAlign="center" verticalAlign="top"/>
</s:HGroup>
<s:HGroup width="100%" verticalAlign="middle">
<s:Label width="100" text="Longitude"/>
<s:Label id="btcLongitude" width="269" textAlign="center" verticalAlign="top"/>
</s:HGroup>
<s:HGroup width="100%" verticalAlign="middle">
<s:Label width="100" text="Accuracy"/>
<s:Label id="btcAccuracy" width="269" textAlign="center" verticalAlign="top"/>
</s:HGroup>
<s:HGroup width="100%" verticalAlign="middle">
<s:Button width="162" label="Stop Recording" click="doGPSSTOP(event)"/>
<s:Button width="162" label="Start Recording" click="doGPSRUN(event)"/>
</s:HGroup>
</s:View>
The updateLocation(…) method actually refresh the geo coordinates on the screen and then calls the storeLocation(…) method in order to keep the information on a local SALLite database.
The doGPSRUN and doGPSSTOP event methods are associated to a button click event and allow us to start (stop) the adobe air mobile Geolocation component. Once started, the Geolocation component delivers the coordinates via an event that is processed in the updateLocation() method.
Notice that we associate the path ID by a value entered by the user. The pathID is therefore a simple information allowing to group a set of coordinates with a name that is going to be used in order to identify (and lately retrieve) a given trail.
The simplistic layout on the Mobile device is reported in the following Image:

Note: More about the Store Remote Button later in this paper.
Heading, Latitude, Longitude and Accuracy will be delivered in real time. The accuracy will specify the gap (potential gap) between your real location and the coordinate delivered by the device. Notice that this may vary from device to device. On my iPad (first generation) I usually get values around 5 to 20 meters. On an iPhone 3GS for example, the accuracy may reach values between 17 and 50 meters.
Why should we use accuracy ? In practice, and as far as we are concerned at least, the accuracy may provide us with an indication of wether we want to keep or discard a result for rendering. As wee will see later on, the Objective C implementation will let us decide since the beginning a "precision threshold" (i.e. we will be able to configure the Geolocation Component in order to be notified if and only if the Accuracy will be below a given desired value.
In the sample implementation (Flashbuilder) we will accept and store each delivered coordinate leaving the option to retain or discard a given record during the rendering process.
The actual work consisting in collecting (locally) the geographical location is taken in charge by the storeLocation(…) method where we extract latitude, longitude and heading and store this information on a local SQLLite Database.
As far as the storage of data is concerned, we can opt for different approaches.
- We can keep the data in an ArrayCollection
- We can store the data in a local file
- We can store the data in a local DB (SQLLite)
The sample code above uses the DB approach. Even if the detailed code is not reported in this article (you can find for example a tutorial on SQLLite and adobe Air here), the implementation is quite simple. All I do in the BTCMobileDBConnector class is open a local SQLLite database file ("btcgeomobile.db") and initialize an Air SQLStatement object.
The BTCMobileDBUtils.appendRecord(…) method simply runs and SQL INSERT statement allowing to store the geolocation informations.
The local SQLLite table has been created using the following SQL statement:
CREATE TABLE "BTCGEOLOCATIONS" ("PATHID" VARCHAR NOT NULL ,"LATITUDE" FLOAT,"LONGITUDE" FLOAT,"ACCURACY" FLOAT,"HEADING" FLOAT)
If you prefer, for the sake of learning, to use a quicker way consisting in storing the data in a local array, you may opt for the following version of storeLocation(…) called storeLocationAsArrayCollection(…) :
var recoredPath:ArrayCollection = new ArrayCollection();
protected function storeLocationAsArrayCollection(geoEvent:GeolocationEvent):void{
var geoItem:Object=new Object();
geoItem.PATHID=btcId.text;
geoItem.LATITUDE=geoEvent.latitude.toFixed(10);
geoItem.LONGITUDE=geoEvent.longitude.toFixed(10);
geoItem.ACCURACY=geoEvent.horizontalAccuracy.toFixed(10);
geoItem.HEADING=geoEvent.heading .toFixed(10);
recoredPath.addItem(geoItem);
}
The recordedPath Array will in this case be filled with each user geo position. The latter approach has however some disadvantages:
- It may run your device out of memory (assuming that you leave the recorder run over a very long period of time)
- It may imply loss of data in case the application would crash.
On the other hand, though, we can still opt for a memory based storage (i.e using the Array) if we consider regular server calls in order to store the current recordedPath on a remote server and this is exactly what will cover in the next paragraph:
Storing Geodata in a remote BlazeDS component:
For the server (java) implementation, I used Hibernate as a persistence framework. The first element that we need is therefore an entity that will enable the storage of data. This entity is reported using java Annotations here below :
package ch.btconsulting.dao;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name="btcgeolocations")
public class BTCGeoLocationItem {
@Id
@GenericGenerator(name="generator", strategy="increment")
@GeneratedValue(generator="generator")
Integer id;
float longitude;
float latitude;
float heading;
String pathID;
public float getHeading() {
return heading;
}
public void setHeading(float heading) {
this.heading = heading;
}
public String getPathID() {
return pathID;
}
public void setPathID(String pathID) {
this.pathID = pathID;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public float getLongitude() {
return longitude;
}
public void setLongitude(float longitude) {
this.longitude = longitude;
}
public float getLatitude() {
return latitude;
}
public void setLatitude(float latitude) {
this.latitude = latitude;
}
}
A geo coordinate (Entity), may now be stored using the following implementation. For the sake of explanations, I use a simple ArrayList as a parameter for the BlazeDS implementation. Recalling the way we store the recorder path (geo coordinates on the mobile device), we will pass therefore the full set of data in one single call.
The BlazeDS (Spring) implementation of the remote java storePath service method is the following:
1: package ch.btconsulting.flex.spring;
2:
3: import java.util.ArrayList;
4: import java.util.HashMap;
5: import java.util.Iterator;
6:
7: import org.hibernate.Transaction;
8: import org.hibernate.classic.Session;
9: import org.springframework.flex.remoting.RemotingDestination;
10: import org.springframework.flex.remoting.RemotingInclude;
11: import org.springframework.stereotype.Service;
12: import ch.btconsulting.dao.BTCGeoLocationItem;
13: import ch.btconsulting.server.hibernate.HibernateUtil;
14:
15:
16: @Service("geolocationServices")
17: @RemotingDestination(channels = { "my-amf", "my-secure-amf" })
18:
19: public class BTCGeolocationServices {
20:
21: public double toDouble(HashMap data, String key) {
22: Object theNumber = (Object) data.get(key);
23: if (theNumber==null) return 0;
24: if (theNumber instanceof Integer){
25: return ((Integer) theNumber).doubleValue();
26: }
27: if (theNumber instanceof Double){
28: return ((Double) theNumber).doubleValue();
29: }
30: return 0.0f;
31: }
32:
33: @RemotingInclude
34: public int storePath(ArrayList geoLocations) {
35:
36: Session session = HibernateUtil.getSessionFactory().getCurrentSession();
37: Transaction trx = session.beginTransaction();
38: for (Iterator iterator = geoLocations.iterator(); iterator.hasNext();) {
39: HashMap object = (HashMap ) iterator.next();
40: BTCGeoLocationItem item = new BTCGeoLocationItem();
41: item.setHeading(toDouble(object,"HEADING"));
42: item.setLatitude(toDouble(object,"LATITUDE"));
43: item.setLongitude(toDouble(object,"LONGITUDE"));
44: item.setHeading(toDouble(object,"HEADING"));
45: item.setPathID((String) object.get("PATHID"));
46: session.saveOrUpdate(item);
47: }
48:
49: trx.commit();
50: return geoLocations.size();
51:
52: }
53:
54: }
Note: I use in the example HashMap to retrieve each individual geo item field but this could be of course replaced with a typed approach i.e. by definition an array of items where the structure would correspond to the one used on the Flashbuilder client.
You may have notice that in order to store the coordinates, I used a conversion method called toDouble(...). The reason is due to the way Numeric are transfered between the Flex Client and the BlazeDS receiving method. A value of a Numeric(10.0) for example is passed to the server implementation as Integer.
A Numeric(10.01) will be however passed as a Double. Since we cannot predict if a given coordinate will have (or not) decimal values, we need to implement withn the java component the logic required in order to cope with both situations.
Storing the geo locations path from the Flashbuilder Mobile Client
We come now to the last part of the Flashbuilder projects consisting in calling the storePath remote method.
In Flashbuilder, the remote client object proxy component can be declared as follow:
<fx:Declarations>
<s:RemoteObject id="BTCGeoService"
endpoint="{BTCAppCommonData.BTCENDPOINT}"
destination="geolocationServices">
<s:method name="storePath" fault="faultHandler(event)" />
</s:RemoteObject>
</fx:Declarations>
I deliberately omitted a result processing method in this case to keep the code as short as possible. In our example, the store path remote method is of type void and does not return any particular value. The fault method would be invoked in case of server errors (delivering eventual remote Java exceptions).
We are all set now to store, at any time, the full set of recorded geolocations in one single call. Recalling the Flashbuilder client implementation above, we may for example call the storePath method with an associated Button. The code (extract) is the following:
<fx:Script>
<![CDATA[
…..
var recoredPath:ArrayCollection = new ArrayCollection();
protected function storePath_clickHandler(event:MouseEvent):void
{
btcDbConnector.btcOpenDb("assets","btcgeomobile.db");
btcDbConnector.btcExecuteSQL("select * from BTCGEOLOCATIONS");
recoredPath=btcDbConnector.btcData;
BTCGeoService.storePath(recoredPath);
}
]]>
</fx:Script>
<s:Button label="Store Remote" click="storePath_clickHandler(event)"/>
Where recordedPath is the ArrayCollection containing All the recorded user geolocations. Here again, the details related to the SQL Select statements are omitted but quite simple to implement.
We have now covered the implementation approach using Flashbuilder.
We will consider in the following paragraphs the corresponding native Objective C implementation and keep, as a goal, to reuse the server BlazeDS implementation.
The Objective C Geolocation recorder component
From the concepts perspective, we will use the same Approach and concepts as we did with Flashbuilder. Apple provides developers with the same Geolocation facilities (in fact, Flashbuilder uses a subset of what we can use within Xcode and Objective C).
In this part , I will however report the basic components and not the full Client Application. As you may know, each Objective C component is usually declared with 2 Files. An header File describing the interface (.h) and a corresponding implementation file containing the logic (.m).
The Geolocation coordinates will be collected by a singleton implementation called BTCGeoManager. Once referenced in an Objeciive C program, this component will immediately start to retrieve the user geo location coordinates. In this respect, the Singleton can therefore be used as is in a client implementation.
What we need to start with is a data transfer object allowing to store each individual geolocation. We will pass an array of this object (similar to the recordedPath in Flashbuilder) to the remote storePaht BlazeDS object.
The header FIle of the geolocation DTO is reported here below:
//
// BTCGeoLocationItem.h
#import <Foundation/Foundation.h>
@interface BTCGeoLocationItem : NSObject{
float latitude;
float longitude;
float accuracy;
NSString *pathId;
}
- (id)initWithPathId:(NSString *)pathId latitude:(float)latitude longitude:(float) longitude accuracy:(float) accuracy;
@property (nonatomic,assign) float latitude;
@property (nonatomic,assign) float longitude;
@property (nonatomic,assign) float accuracy;
@property (nonatomic,retain) NSString* pathId;
@end
with the resulting component implementation :
// BTCGeoLocationItem.m
#import "BTCGeoLocationItem.h"
@implementation BTCGeoLocationItem
@synthesize latitude;
@synthesize longitude;
@synthesize accuracy;
@synthesize pathId;
1: - (id)initWithPathId:(NSString *)ppathId latitude:(float)platitude longitude:(float)plongitude accuracy:(float)paccuracy{
2:
3: if ((self = [super init])) {
4:
5: [self setLatitude:platitude];
6: [self setLongitude:plongitude];
7: [self setAccuracy:paccuracy];
8: [self setPathId:ppathId];
9: }
10: return self;
11: }
Objective C Geolocation Singleton Component
Due to lack of knowledge in Objective C, some companies tend to consider first, as a default , cross code generators. We do not want to bring another new language in the landscape. Although understandable and in some cases also justified, this may not necessarily drive to the most appropriate decision .
It is relevant at this point to emphasize that implementing a native mobile client with XCode has become, since version 4.2, much more comfortable for developers. Just to mention 2 technical aspects :
- Java developers for example were missing in Xcode the implicit memory management provided by Java. Previous to Xcode 4.2, Objective C developer had to explicitly keep track of memory management in order to reduce or eliminate memory leaks. Since Xcode 4.2 Apple introduced ARC (Automatic Reference Counting) taking in charge the memory management tasks and thus dramatically simplifying the development process.
- Storyboards is another new facility introduced since Xcode 4.2 allowing the designer to quickly set up the navigation and layout of the application.
The singleton Design pattern applied to our geolocation case study is particularly appropriated since enable the access and control of the underlying location manager API's from any view within the Application.
In order to keep the code as compact as possible, I also directly included in the geo location singleton the functionality allowing to store in the SQLLite database , if needed, each coordinate delivered by the underlying IOS coregelocation API's.
The implementation below simply delivers the latitude and longitude of the mobile user. We can also extend the functionalities by retrieving the actually address (Street, City) and this by simply modifying , in one single place, the singleton functionality.
The Singleton header (called BTCGeoManager.h) is implemented as follow;
// BTCGeoManager.h
#import <Corelocation/CoreLocation.h>
#import "BTCGeoLocationItem.h"
#import "BTCDBUtils.h"
@protocol BTCGeoLocationProcessing <NSObject>
- (void)processGeoData:(CLLocation*) data;
@end
@interface BTCGeoManager : NSObject <CLLocationManagerDelegate>{
CLLocationManager *lm;
NSMutableArray *recordedPath;
id delegate;
bool recordingEnabled;
NSString* currentPathId;
}
+ (BTCGeoManager *) getInstance;
- (void) preloader;
- (void)startLocationManager;
- (void)stopLocationManager;
- (void)savetGeoItem:(BTCGeoLocationItem*) itemRow;
- (NSMutableArray*) loadDataWithId:(NSString*) id;
- (void)enableRecording;
- (void)disableRecording;
- (bool)recordingEnabled;
@property (nonatomic, retain) id delegate;
@property (nonatomic,retain) CLLocationManager *lm;
@property (nonatomic,retain) NSMutableArray* recordedPath;
@property (nonatomic,retain) NSString* currentPathId;
@end
And the corresponding member implementation is the following:
// BTCGeoManager.m
#import "BTCGeoManager.h"
@implementation BTCGeoManager
@synthesize lm;
@synthesize recordedPath;
@synthesize delegate;
@synthesize currentPathId;
+ (BTCGeoManager*) getInstance {
static BTCGeoManager* bctGeoManager;
if (!bctGeoManager) {
bctGeoManager = [[BTCGeoManager alloc] init];
[bctGeoManager setCurrentPathId:@"DEFAULT"];
[bctGeoManager disableRecording];
[bctGeoManager preloader];
NSLog(@"btc app bctGeoManager Singleton created!");
}
return bctGeoManager;
}
- (void) preloader {
self.lm = [[CLLocationManager alloc] init];
lm.delegate=self;
lm.desiredAccuracy=kCLLocationAccuracyNearestTenMeters;
lm.distanceFilter=kCLDistanceFilterNone;
[self startLocationManager];
}
- (void)startLocationManager{
[lm startUpdatingLocation];
}
- (void)stopLocationManager{
[lm stopUpdatingLocation];
}
-(void) locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *) newLocation
fromLocation:(CLLocation *) oldLocation{
BTCGeoLocationItem* item=[[BTCGeoLocationItem alloc] initWithPathId:[self currentPathId]
latitude:newLocation.coordinate.latitude
longitude:newLocation.coordinate.longitude
accuracy:newLocation.horizontalAccuracy];
if (recordingEnabled){
[self savetGeoItem:item];
}
[delegate processGeoData:newLocation];
NSLog(@"location manager update");
}
- (NSMutableArray*) loadDataWithId:(NSString*) id {
BTCDBUtils* dbutils = [BTCDBUtils alloc];
NSString* dbpath=[dbutils checkAndCreateDatabasePath:@"btcgeomobile.db"];
[dbutils openDatabase:dbpath];
NSString* sql=[NSString stringWithFormat:@"select PATHID,LATITUDE,LONGITUDE,ACCURACY from BTCGEOLOCATIONS WHERE PATHID='%@'", id];
NSLog(sql);
NSMutableArray* retdata= [dbutils executeQuery:(char*) [sql cString ]];
[dbutils closeDatabase];
return retdata;
}
- (void)savetGeoItem:(BTCGeoLocationItem*) itemRow{
if ([itemRow pathId]==nil) return;
BTCDBUtils* dbutils = [BTCDBUtils alloc];
NSString* dbpath=[dbutils checkAndCreateDatabasePath:@"btcgeomobile.db"];
[dbutils openDatabase:dbpath];
sqlite3_stmt *compiledStatement;
char* sqlinsert="insert into BTCGEOLOCATIONS (PATHID,LATITUDE,LONGITUDE) values (?,?,?);";
if(sqlite3_prepare_v2([dbutils btcDatabase], sqlinsert, -1, &compiledStatement, NULL) == SQLITE_OK) {
sqlite3_bind_text(compiledStatement, 1, [[itemRow pathId] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_double(compiledStatement, 2,itemRow.latitude);
sqlite3_bind_double(compiledStatement, 3,itemRow.longitude);
if(SQLITE_DONE != sqlite3_step(compiledStatement))
NSLog(@"Error while updating. '%s'", sqlite3_errmsg([dbutils btcDatabase]));
sqlite3_reset(compiledStatement);
}
[dbutils closeDatabase];
NSLog(@"PATH AS BLOB SAVED");
}
- (void)saveCurrentPathWithPath:(NSArray*) data{
BTCDBUtils* dbutils = [BTCDBUtils alloc];
NSString* dbpath=[dbutils checkAndCreateDatabasePath:@"btcgeomobile.db"];
[dbutils openDatabase:dbpath];
sqlite3_stmt *compiledStatement;
char* sqlinsert="insert into BTCGEOLOCATIONS (PATHID,LATITUDE,LONGITUDE) values (?,?,?);";
if(sqlite3_prepare_v2([dbutils btcDatabase], sqlinsert, -1, &compiledStatement, NULL) == SQLITE_OK) {
for (BTCGeoLocationItem *itemRow in data) {
sqlite3_bind_text(compiledStatement, 1, [[itemRow pathId] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_double(compiledStatement, 2,itemRow.latitude);
sqlite3_bind_double(compiledStatement, 3,itemRow.longitude);
if(SQLITE_DONE != sqlite3_step(compiledStatement))
NSLog(@"Error while updating. '%s'", sqlite3_errmsg([dbutils btcDatabase]));
sqlite3_reset(compiledStatement);
}
}
[dbutils closeDatabase];
}
- (void)enableRecording{
recordingEnabled=true;
}
- (void)disableRecording{
recordingEnabled=false;
}
- (bool)recordingEnabled{
return recordingEnabled;
}
@end
As this is the case on many Objective C implementations, I defined a delegate method (called processGeoData) allowing us to pass back to the caller each individual processed geolocation coordinate.
I also wrapped within our singleton the operations allowing to start/stop the location manager on the device. Again, this is a deliberate design decision and not an obligation. The idea with this approach is to be able to access and manage the location manager from anywhere within the application.
Last but not least, since this information is also used in different views within the application, I store in the singleton generic information such as the current active pathId d as opposed of passing this information from view to view.
In other words, and just as an example, we can now start the location manager and activate a DB storage recording from anywhere in our application with these 2 single lines of code:
[[BTCGeoManager getInstance] startLocationManager];
[[BTCGeoManager getInstance] enableRecording];
Even if from the design point of view a separate class would be more appropriate for DB accesses, I also added in this singleton, for explanation purposes, also the local SQLLite DB accesses. The loadDataWithId method will allow to select the geocoordinates based on a pathId and the savetGeoItem method is used to insert a single gelolocation record in the BTCGEOLOCATIONS local table.
Nothing specially new actually. The BTCDBUtils component is used here as a convenience component in order to open and close an SQLLite Database.
I report here below the corresponding implementation without any specific additional comments. You may refer to the multitude of samples and tutorial on the internet on the subject.
// BTCDBUtils.h
/
#import <Foundation/Foundation.h>
#import <sqlite3.h> // Import the SQLite database framework
#include "TargetConditionals.h"
@interface BTCDBUtils : NSObject {
sqlite3 *database;
}
-(sqlite3*) btcDatabase;
-(NSString*) checkAndCreateDatabasePath:(NSString*) databaseName;
-(int) openDatabase:(NSString*) databasePath;
-(void) closeDatabase;
@end
with the related member implementation :
// BTCDBUtils.m
#import "BTCDBUtils.h"
@implementation BTCDBUtils
-(sqlite3*) btcDatabase{
return database;
}
-(NSString*) getDatabasepathForDatabase:(NSString*) databaseName{
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString* databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
return databasePath;
}
-(NSString*) checkAndCreateDatabasePath:(NSString*) databaseName{
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL success;
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString* databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
#if (TARGET_IPHONE_SIMULATOR)
databasePath= [@"/my_local_test_path" stringByAppendingPathComponent:databaseName];
// NSLog(databasePath);
#endif
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the database has already been created in the users filesystem
success = [fileManager fileExistsAtPath:databasePath];
// If the database already exists then return without doing anything
if(success) return databasePath;
// If not then proceed to copy the database from the application to the users filesystem
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
return databasePath;
}
// return SQLITE_OK if database succesfuully opened;
-(int) openDatabase:(NSString*) databasePath{
int status = sqlite3_open([databasePath UTF8String], &database);
return status;
}
-(void) closeDatabase{
sqlite3_close(database);
}
Calling the remote BlazeDS Geolocation storePathService
As we mentioned in the article introduction we aim here to reuse the same remote Java service that we implemented for Flashbuilder. I use in the example the CocoaAMF of nesium as also described in this article that you can find on this site.
We don't want to rewrite an additional service in Java for Objective C. I assume that all the CocoaAMF classes are copied in your Xcode project (follow otherwise the instruction provided by cesium here).
As this was the case for the Flashbuilder client, we need a client proxy object to deal with the server communication. This is implemented in one single Objective C class as follow:
// BTCAMFServiceCaller.h
#import <Foundation/Foundation.h>
#import "AMFRemotingCall.h"
@protocol BTCAMFServiceCallerDelegate;
@interface BTCAMFServiceCaller : NSObject <AMFRemotingCallDelegate>
{
AMFRemotingCall *m_remotingCall;
NSObject <BTCAMFServiceCallerDelegate> *m_delegate;
id delegate;
}
- (id)initWithBroker:(NSString *)url usingService:(NSString *)targetService;
// Service methods
- (void)savePath:(NSArray*) arrayData;
- (void)deleteRemotePath:(NSString*) pathId;
- (void)getAllRemotePath;
- (void)getPathData:(NSString*) pathId;
// Delegate methods
- (void) processStorePath:(NSMutableArray*) data;
- (void) processGetPathData:(NSMutableArray*) data;
- (void) processDeletePath:(NSMutableArray*) data;
- (void) processGetAllPathNames:(NSMutableArray*) data;
// Nesium specific protocol
- (void)callerDidFinishLoading:(BTCAMFServiceCaller *)caller receivedObject:(NSObject *)object;
- (void)caller:(BTCAMFServiceCaller *)caller didFailWithError:(NSError *)error;
@property (nonatomic, retain) id delegate;
@end
@end
The interface defines actually 3 categories of methods (I will hoverer describe only the savePath in details).
The first category of methods, that I referenced as "service Methods" is used by the Client implementation to actually call the remote services (savePath is an example).
The second category (delegates - BTCAMFServiceCallerDelegate) allows the caller component to be notified about the completion and result of the remote call. This are the equivalent of the service Handler functions in Actionscript (Flashbuilder). In the case of the savePath method, we will use it to inform the user that the recorded Path has been, hopefully, successfully stored on the remote database.
The third category (CocoaAMF specific) are used by the proxy service component to process the data returned by the CocoaAMF layer.
In other words, we use our custom BTCAMFServiceCaller component as a local service dispatcher and wrapper.
The resulting implementation is the following :
// BTCAMFServiceCaller.m
#import "BTCAMFServiceCaller.h"
@implementation BTCAMFServiceCaller
@synthesize delegate;
- (id)initWithBroker:(NSString *)url usingService:(NSString *)targetService
{
if (self = [super init])
{
m_remotingCall = [[AMFRemotingCall alloc] init];
m_remotingCall.URL = [NSURL URLWithString:url];
m_remotingCall.service = targetService;
m_remotingCall.delegate=self;
}
return self;
}
- (void)savePath:(NSArray*) arrayData{
m_remotingCall.delegate=self;
m_remotingCall.method = @"storePath";
m_remotingCall.arguments = [NSArray arrayWithObjects:arrayData,nil];
[m_remotingCall start];
}
- (void)deleteRemotePath:(NSString*) pathId{
m_remotingCall.delegate=self;
m_remotingCall.method = @"deletePath";
m_remotingCall.arguments = [NSArray arrayWithObjects:pathId,nil];
[m_remotingCall start];
}
- (void)getAllRemotePath{
m_remotingCall.delegate=self;
m_remotingCall.method = @"getAllPathNames";
m_remotingCall.arguments = [NSArray arrayWithObjects:nil];
[m_remotingCall start];
}
- (void)getPathData:(NSString*) pathId{
m_remotingCall.delegate=self;
m_remotingCall.method = @"getPathData";
m_remotingCall.arguments = [NSArray arrayWithObjects:pathId,nil];
[m_remotingCall start];
}
- (void)remotingCallDidFinishLoading:(AMFRemotingCall *)remotingCall
receivedObject:(NSMutableArray *)object
{
NSString* callermethod=(NSString*) remotingCall.method;
if ([callermethod isEqualToString:@"deletePath"]==true){
[delegate processDeletePath:object];
}
if ([callermethod isEqualToString:@"storePath"]==true){
[delegate processStorePath:object];
}
if ([callermethod isEqualToString:@"getPathData"]==true){
[delegate processGetPathData:object];
}
if ([callermethod isEqualToString:@"getAllPathNames"]==true){
[delegate processGetAllPathNames:object];
}
}
- (void)remotingCall:(AMFRemotingCall *)remotingCall didFailWithError:(NSError *)error
{
NSString* callermethod=remotingCall.method;
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"REMOTE SERVER ERROR"
message:@"Connection Failed"
delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
NSLog([error description]);
[alert show];
}
@end
The remotingCallDidFinishLoading method is therefore used as a central handler dispatching the results based on the method name used.
Implementing the Objective C Client using the Xcode 4.2 and storyboards
With Xcode 4.2, Apple introduced a new way to design the client interface. The task of creating and linking view has been greatly simplified with Storyboard.
Besides an easier implementation, Storyboards provide an excellent mean to get a better overview on the whole application and also to quickly set up the client navigation. Throgh so called Segue connections, the developper can not only design but also directly run a draft version before views are effectively implemented. This may allow for example a user to concretely get a first feeling on the final applicationn will react in terms of navigation.
In order to represent this aspects, let's consider the following image representing the sample demo application that I prepared to set up this paper:

Besides the link among views, the image above provides example for the following items:
- It shows the views that we are going to implement (Geo Path View Controller) and path DetailView Controller)
- It shows the navigation from the main Window
- It shows a view that is palnned but not yet implemented (Settings). This view can however be already used and tested by the user on the mobile device although we have not yet started with a concrete implementation. On the other hands, this give us the opportunity to show the user where he/she will be in a position to manage the application settings.
Implementing the Geo Path Recorder View
In this part of the article I will show the details related to the record path view (highlighted in the above image) i.e. the view responsible to enable/disable the DB storage of geocordinates and allowing to directly show on a MapView (GoogleMap) the user location.
From the implementation point of view, this will be reduced to invoke Mehtods of the GeoLocation Singleton component described above. When the sample application is running, this view looks like something similar to this:

The Swithc button (Follow) enable to follow the user position in real time (we will see how to do this in a moment). The DB Rec. switch button enables to activate/disactivate the storage of geo location in the local SQLLite Database.
Lat/Lon and Acc display the actual latetude, longitude and accuracy of the last/current location delivered by the Apple LocationManager component.
Last but not least, the PathId is an Input text filed where the user can allocate a given Id to the current path recording.
Well let's see now the Object C part of the story:
The view Interface declaration is the following :
// BTCGeoPathViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import "BTCGeoManager.h"
@interface BTCGeoPathViewController : UIViewController <BTCGeoLocationProcessing>{
IBOutlet MKMapView *mapView;
IBOutlet UILabel *latitude;
IBOutlet UILabel *longitude;
IBOutlet UILabel *accuracy;
IBOutlet UITextField *currentPathId;
UISwitch *userFollowSwitch;
UISwitch *storeDBSwitch;
}
- (IBAction)actionPathIdChange:(id)sender;
@property (nonatomic,retain) MKMapView *mapView;
@property (nonatomic,retain) IBOutlet UISwitch *userFollowSwitch;
@property (nonatomic,retain) IBOutlet UISwitch *storeDBSwitch;
@property (nonatomic,retain) IBOutlet UITextField *currentPathId;
@end
Besides the label and TextField, we recognize the Switch Properties allocated to the actions of enabling/disabling storage and realtime user location display.
MMapView is the standard Component allowing to render the location on a GoogleMap.
Using the Storyboard, we only need at this point to allocate the SwitchButtons to the Singleton methods. And here is the code:
// BTCGeoPathViewController.m
#import "BTCGeoPathViewController.h"
BTCGeoManager* geoManager;
@implementation BTCGeoPathViewController
@synthesize mapView;
@synthesize userFollowSwitch;
@synthesize storeDBSwitch;
@synthesize currentPathId;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
geoManager = [BTCGeoManager getInstance];
[geoManager setDelegate:self];
[currentPathId setText:[geoManager currentPathId]];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)processGeoData:(CLLocation*) newLocation{
[accuracy setText:[NSString stringWithFormat:@"%f", newLocation.horizontalAccuracy]];
[longitude setText:[NSString stringWithFormat:@"%f", newLocation.coordinate.longitude]];
[latitude setText:[NSString stringWithFormat:@"%f", newLocation.coordinate.latitude]];
MKCoordinateSpan span;
mapView.mapType=MKMapTypeHybrid;
if (userFollowSwitch.on) {
span.latitudeDelta=0.0011;
span.longitudeDelta=0.0011;
MKCoordinateRegion region;
region.center=newLocation.coordinate;
region.span=span;
[mapView setRegion:region animated:YES];
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (IBAction)dbStoreSwitchChanged:(id)sender {
if (storeDBSwitch.on) {
[geoManager enableRecording];
[geoManager setCurrentPathId:currentPathId.text];
} else{
[geoManager disableRecording];
}
}
- (IBAction)actionPathIdChange:(id)sender {
[geoManager setCurrentPathId:currentPathId.text];
[self.currentPathId resignFirstResponder];
}
@end
In the viewDidLoad method we initialize the BTCGeoLocation singleton and initialize the currentPath identifier with the current value of the singleton Path Id.
We may recall at this point the processGeoData method that I used in the Singleton protocol as a delegate method. The singleton, if the location manager is active, will call this method each time a new geolocation is delivered by the mobile device geolocations system. In this method, we construct a MKCoordinateSpan instance that we fill and allocate to the MapView in case the user wants to see his/her current location on the Map.
The actionPathIdChange method is associated with the DB Rec switch button change Event and will enable/disable the DB storage based on the corresponding methods of the singleton described above.
Calling the remote BlazeDS service from Objective C
In this last paragraph, I will provide the hints related to how we can invoke now the remote BlazeDS service from our Objective C mobile Application. I will however at this point only describe the most relevant pieces of code.
Since we implemented in the singleton the method allowing to retrieve all the coordinates from a given path in the BTCGeoLocation component, the code required in order to retrieve and send to the remote server a given path is reduced to the following IBACTION that we may associate everywhere in our
Application:
- (IBAction)savePathOnServer:(id)sender {
BTCGeoManager* geomanager = [BTCGeoManager getInstance];
NSMutableArray* dataToSend=[geomanager loadDataWithId:[geomanager currentPathId]];
[m_caller savePath:dataToSend];
}
And at this point we are all set to record locally and store remotely geocoordinate information from either a Faslbuilder Moble application or a native Objective C program using the same common BlazeDS AMF based java service.
Conclusions:
This article had the aim to provide a rough idea on what we can achieve on a mobile device using 2 different development environments and technologies. The choice of going native or opt for a cross generation solution is not always easy and there is probably no ultimate decision criteria. The article showed, I hope, how easy and compact a Flasbuilder approach can be. On the other Hands, a native solution with objective C provides, out of the box, a broader set of API's and possibilities.
SInce Native extensions are now also enabled in Falsh Builder (in particular since version 4.6 these are directly supported in the development environment), a combination of both approaches will match at best most of corporate requirements. It is in any case more than recommended, independently from the cross generation tool selected, to build internal know how also on native implementation and Objective C, even if not used within a full native implementation, is in this respect a must for targeting IOS devices.