
In this topic, we want to share our experience in creating mobile applications on the platform for developing cross-platform applications Titanium. Around 2011, we began working with cross-platform frameworks. At first it was PhoneGap, then Titanium. We made a dozen applications that still work, both in Russia and in the USA. We consciously want to move away from evaluations - it’s bad or it’s good to develop cross-platform applications, and focus on the difficulties that we have to face in terms of developing and maintaining these applications.
In our opinion, the topic will be useful both to readers who are going to order the application, so that they can make a choice between native-development on each platform and cross-platform, as well as to developers who decide on where to go.
So, let's start with a list of problems you will have to face.
')
Problems:
- The problem of double clicks.
- If code.
- Memory management on Android.
- Insufficient implementation of individual functions, including standard ones.
- Javascript - the lack of typing slows down the process of writing code and complicates maintenance.
- Absence InterfaceBuilder - slows down the process of writing an application, the entire UI is written in code.
- Titanium SDK updated later operating system SDK.
- Each version of the SDK contains fixes for old bugs and introduces new bugs.
Examples in which we encountered these problems.
Double click issue
The mobile application contains various controls - buttons, text input fields, switches, etc. When a user clicks on any of them, the application receives a signal about this in the form of an event - an object containing information about which element the action was performed with, what action it is, such as a long or short press, and another. In native applications, i.e. in applications written using standard development tools (iOS SDK for iPhone and Android SDK for Android), the corresponding control is blocked during event processing and a new event cannot come from it. Probably, everyone noticed that if you click on the button for sending a message in a standard application, then for a while the button becomes gray and you cannot click on it a second time. Such blocking in native applications occurs automatically and does not require a programmer to write code or other actions. In an application written using the Titanium SDK, there is no such lock, so each control can send several events of the same type. If, for example, by pressing a button, a new application screen opens, then two or more screens may open.
This is wrong and inconvenient for the user. Apple, most likely, will not even miss such an application in the AppStore. It is necessary to block the UI or use the checkboxes in order to ignore subsequent events until the first one is finished processing. Our testers called this problem a double click issue.
We came up with several ways to solve this problem:
1. In the first version, we made a screen lock: after the first click, the entire screen of the application is closed with the message “Please wait ...” on a translucent gray background. For simple situations, the message is hidden after a certain time (about half a second). For longer operations that require, for example, data loading, it is necessary to lock the screen indefinitely, and then at the end of the event processing a special screen unlock function is called that hides the message. The message itself serves as a shield for subsequent user actions.
It looked like this:

2. In another embodiment, it is checked that at least the specified time interval has passed since the last click. If half a second since the last click has passed, the event is processed, otherwise it is ignored.
In both cases, this is less clear to the user than the behavior of the native application, in which the button simply cannot be pressed during the processing of the previous event.
If code
Many things for different operating systems are implemented using various functions, properties and modules, in the program code you constantly have to write “if this is Android, then we do it, and if the iPhone, then this is it”. First, the variable is started to write less code, its value is filled at the beginning of the application:
if(Titanium.Platform.name.indexOf('iPhone') >= 0) { isIPhone = true; isAndroid = false; }
And you start to insert checks everywhere.
retrieving objects from the database: if(isAndroid) { fieldCount = resultSet.fieldCount; } else { fieldCount = resultSet.fieldCount(); }
This is a very wonderful example. First of all, it shows how much attention you need to pay to small things when you work with Titanium. Secondly, the documentation was first written incorrectly, so the developers had to look at the Titanium source code to fix the code of their application. In general, the Titanium documentation usually states which operating system this or that parameter works for:

If Android is drawn under the parameter, it means it works in Android applications, if an iPhone or iPad is drawn, then this parameter is for iOS. And there are a lot of such notes in the documentation.
Sections in tablesIf you update the data in the table with sections on Android, the application will fall. Since sections on Android do not know how to behave as beautifully as on iOS, staying at the top of the screen while scrolling through the list under the section, we inserted the section view into the first list item for each section. Then the application does not fall.
Text display - labels, and text entry fieldsAndroid indents within text fields, and these indents depend on the version of Android, on the size of the text field and the font size in it. The usual problem is invisible text, which seems to be included in the text field, but not included, due to these internal indents. If you want everything to look equally good, you will have to write a method with a large number of ifs, which will determine how to display the text now.
Hidden text var createLabel = function(properties, linesCount) { var fontSize = isNotEmpty(properties.font) && (properties.font.fontSize) ? properties.font.fontSize : defaultFontSize; var offset = Math.floor(fontSize/8); var heightOffset = 2 * offset; var lineHeight = fontSize + heightOffset; var androidTopOffset = 0; if (isAndroid) { androidTopOffset = (fontSize <= getControlSize(18)) ? Math.floor(fontSize / 4) : Math.floor(fontSize / 11); } if(isNotEmpty(properties.top)) { properties.top = properties.top - offset - androidTopOffset; if(isNotEmpty(properties.height) && properties.height != Ti.UI.SIZE) { properties.height = properties.height + heightOffset; } else if(isNotEmpty(linesCount)) { properties.height = lineHeight * linesCount; } } if (isEmpty(properties.font)) { properties.font = {}; } properties.font.fontFamily = fontName; properties.font.fontWeight = 'normal'; if (isEmpty(properties.wordWrap)) { if (isNotEmpty(linesCount) && linesCount == 1) { properties.wordWrap = false; } else {
Special attention should be paid to the DEBUG section: if the text does not have a color, we set the color to red. This is done because the default color values ​​for the iPhone and Android are different and you need to always write the color to each label - if you want the application to look the same on different devices. It is also necessary to calculate the size of each element, because the screens of Androids are very different, and the size of the font and controls must match the size of the screen. Because of such discrepancies between the platforms, the application must be tested on all devices and operating systems, and even when something is fixed for one of the platforms — it is sometimes impossible to predict how the correction will affect other devices.
Android Memory Management
When most of the application was already written, we faced a very serious problem when testing on Android: with active work in the application, it drops after 10 minutes. It turned out that for each screen a large chunk of memory is allocated, which is not released even when the screen is closed. This problem has not been completely resolved, the latest version that was tested on, 3.1.1. To increase the stability of the application, we had to reduce the interface design, remove all background images, reduce the number of elements, rewrite some standard controls.
If the application needs to download data from the Internet, then there may also be a problem. If the application does not have enough memory, then when the data is loaded, the application will crash. In the native Android application, the developer can add exception handling, errors that occur during the operation of the application, you can even display a message about the lack of memory to the user (although it is preferable to solve the problem in some other way). When you write on Titanium, this is not possible. When loading data, an error occurs deep inside Titanium and cannot be processed by software.
Insufficient implementation of individual functions, including standard ones.
Probably the largest number of such problems we have collected with the implementation of the ability to add photos in the application on Android. The first problem we encountered was the inability to write code to exit from the photographing mode. Those. If the application should show the screen of photographing with a couple of buttons "Make a photo" and "Cancel", then by pressing the cancel button you can not cancel anything. The second problem is the impossibility to know that the user has left the photo mode by pressing the “Back” button on the phone. Those. If you need to update the interface upon exiting the photographing mode, you cannot do this.
Many of these shortcomings and problems are on the list of Titanium bugs for several versions.
jira.appcelerator.org/browse/TIMOB-16182jira.appcelerator.org/browse/TIMOB-16199General list of problems:
jira.appcelerator.org/secure/IssueNavigator.jspa ?
Javascript - lack of typing, slows down the process of writing code and complicates the maintenance
Most modern mobile developers are used to writing programs in high-level languages, such as Java and Objective-C, but Javascript has to be written on Titanium. This is more familiar to web developers, but they do not know the basics of mobile development, have not encountered the limitations of memory and the rules for creating interfaces for mobile applications. Javascript is a language with dynamic typing, which means that a variable is associated with a type at the time of assigning a value, and not at the time of declaring a variable. Thus, in different parts of the program the same variable can take values ​​of different types. For a programmer, this means that he must make sure that the program works with the data in accordance with their types, because otherwise the application will fall.
Lack of InterfaceBuilder - slows down the process of writing an application - the entire UI is written in code
Many mobile applications contain lists: Twitter, news feed, letters in the mailbox, address book, reminders, search results - these are all lists. Usually the screen with the list is implemented on the table. Each element is a cell in which a template is described. For an iOS application, a programmer creates a template (or various templates for complex tables) in Interface Builder

And writes the code to display the data in this template:
- (void)updateViews { if (!self.package) return; static NSDateFormatter* df = nil; if (!df) { df = [[NSDateFormatter alloc] init]; df.dateStyle = NSDateFormatterShortStyle; df.timeStyle = NSDateFormatterNoStyle; } self.labelCity.text = self.package.city; self.labelPackageId.text = self.package.formattedPackageId; self.labelItemsCount.text = @(self.package.items.count).stringValue; self.prizeTypeImageView.image = [self prizeTypeImage]; self.labelDispatchDate.text = [df stringFromDate:self.package.dispatchDate]; self.labelPeriod.text = [NSString stringWithFormat:@"%@ – %@", [df stringFromDate:self.package.periodBegin], [df stringFromDate:self.package.periodEnd]]; }
On Titanium, you need to write everything in code, it looks like this:
Hidden text var rowTemplate = function(item, index, callback) { var row = Ti.UI.createTableViewRow({ height: rowHeight, left: 0, right: 0, selectedBackgroundColor: '#11a2c5', backgroundSelectedColor: '#11a2c5', color: 'transparent', className: classNameStr }); var view = Ti.UI.createView ({ top: 0, left: leftOffset, right: rightOffset, height: rowHeight, color: 'transparent' }); var backView = Ti.UI.createView ({ top: 0, left: 0, right: voucherWidth, height: rowHeight, color: 'transparent' }); view.add(backView); var imageView = Ti.UI.createImageView({ top: top, left: 0, width: getControlSize(47), height: getControlSize(58), image: getPicturePath('/images/orders/icon_oteli_mini') }); backView.add(imageView); var left = imageView.left + imageView.width + topOffset; var nameLabel = createCommonLabel({ left: left, top: top, right: topOffset, height: imageView.height, font: { fontSize: fontSize }, color: '#000000', text: itemName }, 2); backView.add(nameLabel); top = imageView.top + imageView.height; var placeLabel = createCommonLabel({ left: 0, top: top, right: 0, height: height, font: { fontSize: fontSize }, color: '#000000', text: itemCity }); backView.add(placeLabel); top = placeLabel.top + placeLabel.height; var dateLabel = createCommonLabel({ left: 0, top: top, right: 0, height: height, font: { fontSize: fontSize }, color: '#000000', text: datesText }); backView.add(dateLabel); var voucherView = Ti.UI.createImageView({ width: voucherWidth, top: topOffset, right: 0, height: getControlSize(48), backgroundImage: getPicturePath(voucherImagePath), visible: voucherActive }); view.add(voucherView); top = dateLabel.top + dateLabel.height; var numberLabel = createCommonLabel({ left: 0, top: top, right: 0, height: height, text: orderNumber, font: { fontSize: fontSize }, color: '#000000' }); backView.add(numberLabel); top = numberLabel.top + numberLabel.height; var statusLabel = createCommonLabel({ left: 0, top: top, right: 0, height: height, font: { fontSize: fontSize }, color: '#000000', text: statusText }); backView.add(statusLabel); return row; };
In Titanium 3.1.0, there is a new opportunity to implement the list - ListView. On Android, it works significantly faster than a table, and the implementation of lists on a ListView allows you to make an application that will not slow down and freeze when scrolling through the list. To do this, you need to rewrite the creation of a cell as a template:
Hidden text var listItemTemplate = { properties: { height: rowHeight, backgroundSelectedColor: '#11a2c5' }, childTemplates: [ { type: 'Ti.UI.View', bindId: 'rootView', properties: { left: leftOffset, top: 0, right: rightOffset, backgroundSelectedColor: '#11a2c5', height: rowHeight, color: 'transparent' }, childTemplates: [ {
Since the implementation on ListView completely changes the screen creation code of an application, it makes sense to put the creation of this screen for iPhone and Android into various files and add conditions for connecting one of them according to the platform on which the application is launched.
Titanium SDK updated later operating system SDK
Not so long ago, Apple released iOS 7, where the graphics were significantly reworked, work with the status bar (top of the screen, where the battery charge level, time and operator are displayed), and more.
How does a new operating system usually go on the market? First, third-party developers, i.e. Mobile application developers are given access to the beta version of the new SDK to create applications for the new operating system. Those. Anyone wishing to release an application that supports the latest features of the system can and should prepare in advance. Then a stable SDK appears and they begin to be accepted for testing in the AppStore of the application with support for the new version of iOS. Then, users - owners of iPhones - become available to update the operating system and have had time to prepare applications.
What happens to those who develop the application on Titanium? The first beta version of the iOS SDK is available to developers from June 11, 2013, the version of the Titanium SDK with iOS beta support is from August 15 (SDK 3.1.2). Applications with support for the new version of iOS were accepted for review from September 11, 2013.
The operating system has been available to users since September 18, 2013: the developers had a week to go through the Apple check.
Titanium released version 3.1.3 RC on September 9, and stable version 3.1.3 with iOS 7 support - only on September 18, i.e. when the moment to be first in the market was already irretrievably missed.
In version 3.1.2, support for the normal behavior of StatusBar was not implemented. First, if the application used to work in full-screen mode, then now it retreats from the top edge of the screen to the size of the bar status, and the contents of the screens may be completely unexpected, depending on how the coordinates of the elements were set when creating the application. But even if you decided to make an application that does not work in full screen mode, the status bar would still not work as expected - instead of the bar status, a black bar is displayed without any information: neither the clock, nor the charge, nor the operator is displayed on it . With the release of Titanium 3.1.3, the situation has noticeably improved, however, in the photographing mode, a status bar appears even with the full-screen application
jira.appcelerator.org/browse/TIMOB-15203 - this error was fixed only in Titanium version 3.2.0, which was released on December 20.
Each SDK version fixes old bugs and introduces new bugs.
Using the example of the same version 3.1.3: when switching to it from version 3.1.2, the animation in the application slowed down significantly. For example, we need two interface elements to move smoothly from left to right at the same time. In native applications, the developer can describe all the animations that need to be applied to the interface elements and start the animations to run simultaneously. In previous versions of Titanium, the description of animations for individual elements was sequential, but it was launched almost simultaneously, so it was visually perceived as one animation acting with several objects. In Titanium 3.1.3, the time lag between sequentially running animations has become very noticeable and can be 1-3 seconds. Upon completion of the animation, it is usually necessary to perform some operations, change the position of the elements (the animation itself does not change them), delete objects that are no longer visible, so that they do not take up memory. In Titanium 3.1.3, the animation postprocessing call does not always happen. Want to fix the status bar - update the SDK version. Yes, your animation will stop working. So what. Update further. You can simply never make an application that can be tested by Apple, waiting for the Titanium bugs to be fixed. And you can not even collect it.
In our practice, there was a case when the update of Titanium Studio - the development environment used to build applications - led to the fact that the function of assembling an application for publication simply stopped working:
jira.appcelerator.org/browse/TC-1322 (SDK 2.1.3 RC2 will not build for iTunes store distirbution) or
jira.appcelerator.org/browse/TC-2193 (Studio 3.1 Distribute for iTunes Store is producing ad-hoc builds) - when trying to build an application for publishing on the App Store, an application for AdHoc distribution is collected - in this case, helped reinstall the studio.
In conclusion, I would like to note that someone can say that all this is nonsense and problems can be circumvented. The only question is whether it is necessary to do it and whether it should be done at the expense of the quality and convenience of the applications, lagging behind the market, constantly increasing maintenance costs We decided for ourselves that it was not worth it - specialized tasks needed to be solved with tools created for this.