[url=http://zenborgium.blogspot.com.au/2012/01/how-to-not-to-build-and-structure-large.html]http://zenborgium.blogspot.com.au/2012/01/how-to-not-to-build-and-structure-large.html[/url]
[b]Why to structure application[/b]
Well, you could put all source code files in same (root) application directory and everything would work fine. The thing here is ... when application grows, it becomes difficult to find particular file or to know where particular part of application is located if files in it are not organized in some meaningful way (for initial application developer, but also for all developers that will work on application at some time).
That's human nature. If you imagine large number of books, finding particular one can be difficult if they are not organized in any meaningful way. Search for particular book can be accelerated if books were sorted alphabetically. Human will search from left to right for first letter first and then for particular book. If books were sorted vertically in rows by first letter, human could find particular one even more faster because letters are clearly separated.
So, organizing things can be beneficial and the way they are organized is important.
That's why good application structure can be very useful. And not only application structure, but conventions in application also.
[b]Why "NOT to"[/b]
The answer is simple. Latest versions of Titanium framework introduced CommonJS standard that brought some new functionality, while changed old. That's why this way of structuring application is no longer good (and won't work in future SDK versions), but similar principles can be translated to new, CommonJS, functionality (and I'll show how).
[b]Basic concepts in my library[/b]
To understand application structure that I use, I need to explain some basic concepts that I use. This approach might not suit your needs, but it might give you some directions or ideas how to build your library for Titanium framework or how to structure your own application.
I'm using wrapped Object object as basic component. That component is basically container that has methods like add, remove, get, removeAll and getAll.
View is special component that is wrapper around Titanium UI View component. I wrote wrapper for every Titanium UI component (so, I don't use generic wrapper). Every wrapper can do everything what basic component can do, but beside that it proxies Titanium functionality (for example it adds or removes Titanium UI components in the background) and alters Titanium provided functionality where necessary (workarounds, simpler or enhanced usage, ...).
A good thing about this approach is that you can use this wrappers with native JS functionality without a fear that something will brake because they are native JS objects and not Titanium host objects. Titanium host objects in many cases do not behave like native JS objects and it's dangerous to do anything with them that is not documented. Also, this wrappers provide secure layer in cases when Titanium API changes or brakes (which is not so rare case). If you write code that depends directly on Titanium API, you could eventually end up in rewriting a lot of code. For example, "children" property in recent version was removed from some components. It is not so rare case that we need to get all child components of some component. If you depend directly on Titanium API, you would need to rewrite every part of your application that uses that property.
To simplify functionality of getting particular component, I add its reference as a property.
I use string names to differentiate component instances of same type because it is easier to remember string component name, than integer index position in array of child components. Also, code that uses child component is not dependent on position in parent component so position can be changed without consequences.
I make component for everything that can be reused:
"Toolbar" can again become some kind of component (everything is a component):
This approach is very similar to the one that can be found in EnyoJs (http://enyojs.com/). So, basic idea is to build small components first and then use them to build more and more complex components until whole UI is built. This approach has showed to be very flexible and scalable. Also, once all low level components are built, building higher level components is easy task.
I implemented some things that Titanium framework lacks, like FormView and GridView. FormView is awesome component, basically, every component it contains and that implements "setValue" or "getValue" method FormView can simply use to set its value and to get its value. That way I made that component very generic and it can use literally anything that somehow implements that methods (for example, I use table view in forms, table view is actually collection of data that can be dynamically added or removed). FormView component also implement that methods, so they can be nested.
Basic application would look like this:
Context is window abstraction. It is different from all other components because it is important for memory management. Before 1.8 and especially before 1.7 SDK, Android part of Titanium was unstable and building something complex was mission impossible. Tom Cruise in me made this Context window abstraction that would remove all components and their events when window would close. From what it seemed to me at that time, in some cases, some event listeners were not removed so after window was re-created and re-opened, application would crash. By removing events manually, application would not crash. Also, at that time, many people had memory leak problem that sometimes just could not be solved even with some workarounds that came from community (workarounds that I tried did not work for me). By removing components manually, I did not have memory problems. From what my tests shows, 1.8 has much better memory and event handling and I actually don't use this features any more.
Removing components is easy task when there are wrappers that have "getAll" method, but what about listeners? In Titanium, event system is very basic and don't have much features. Also, Titanium component's "listeners" property was the only way to get list of all listeners, but it was unfortunately removed (partially, at least).
That's why I implemented my own publish/subscribe pattern.
Internally, Events is a container that can register events, publish events, remove (all) events, remove events by particular event type and so on.
In the background, I don't use "listeners" property, but my own container, similar to the one I use for basic component. Events are registered by event type and because of that it is easy to remove all events associated with particular event type. Also, to remove events by some event type, there is no need to store anonymous function anywhere (it is stored internally in "Events" container).
If first argument is omitted, app-level event is created. App-level events are dangerous to use because they they remain in memory all the time application runs and they can potentially create memory leaks, so functionality described above is great to have in cases then app-level events are used (all app-level events can be removed with one statement).
With all this functionality, removing events is trivial task.
Because I don't manually remove events when context closes anymore (starting from 1.8), some extra measures are needed to be done in this implementation to prevent memory leaks. I found that this event system is limited (and IMHO it forces some bad practices) and I have a design idea for new, more advanced, event system that would suit better to usage with my library.
[b]Namespaces and naming convention[/b]
To easily name and find components, I split components to different namespaces. I have one root namespace that has subnamespaces.
Concrete implementation of some component would be added to particular namespace where it belongs:
Component name, as every other function constructor, starts with capital letter, while namespace names are lower case.
I made convention that component's code is located in one file and that component namespace name is mapped to file path relative to "Resources" directory and file name. For example Toolbar component's code would be stored in toolbar.js file. Since Toolbar is in "components.ui namespace", toolbar.js file would be located in "Resources/components/ui" directory (FormView component's file name would be form_view.js).
I never put concrete, instantiated component to namespace, because that's a bad practice that might work on iOS, but brakes on Android. Using one component, or even more, adding one component to different windows (even if that doesn't happen in the same time) can have strange side effects. The best approach is to create twin-component whenever it is needed by using function constructor.
To load particular component, I use custom loader:
That method first checks if component is already present in the namespace, if not it includes it:
Every component can have default preferences, which I store to global configuration object (although, code is located in component's file):
which is usually merged with configuration object passed to component's function constructor:
That way other components can change default behavior of some component by passing configuration object modifications as function parameter.
[b]MVC[/b]
To separate concerns and to make simple implementation of MVC pattern, I separated how components are defined in the interface and how do they behave (this is actually very useful when one component is used in different situations and therefore they have modified behavior but same appearance).
To do this, I enhanced some Titanium wrappers so they can automatically add other components through configuration object. That is done by convention, configuration object can have special "items" property which defines components that will be automatically added:
This basically is used for "View" part of MVC.
"View" is defined through configuration object defined for every component.
For example, "View" part of ActionBar component would be:
To make Controller part of MVC, View part has to do something. I defined that before in when I described "Events":
Besides interaction, Controller is responsible for component construction, parameter passing, gluing components together, passing data to View, getting data for View and so on. Controller is what gives life to View, View is stupid as it can be (it's just definition through configuration object).
"Model" part is up to you. :)
I use modified version of Joli ORM (https://github.com/xavierlacot/joli.js/) and Sqlite for database models.
[b]Application structure[/b]
Application structure has three main parts: library, components and modules.
In library, there are all common things, like functionality for including components, creating namespaces, events system definition, database ORM and so on.
In components there are all general components that have no specific data in them. That components can be reused in multiple projects. For example, there are UI components (Button, Toolbar, FormView), filesystem component, XML component, ...
[quote]/components/ui/button.js[/quote]
In modules there are all project-specific components with data in them grouped into modules and their submodules where necessary (for big modules it's handy to group them into submodules). This components are general components with specific data in them. For example, if Button is general component, specific component would be LoginButton which is Button component with specific title.
[quote]/modules/user/ui/login_button.js[/quote]
View part is stored in "ui" namespace, while Controller is stored in module's root directory. Beside that two, I store models in module's directory also.
[quote]/modules/user/models/db/users.js[/quote]
Common practice that many people follow is not to separate this two kinds of components (with and without specific data), but I think it is better to separate them. If you mix project specific data with general components, then it will not be possible to reuse them in future projects. By separating them clearly, you can just copy components to new project (or link common library when that functionality become available in TiStudio) and start working on project specific modules.
[b]Translation to CommonJS[/b]
I'll show you how I easily translated to CommonJS standard from this namespace-based application structure.
The biggest problem in CommonJS implementation in Titanium framework is that it does not have global variable implementation. As you might have noticed, namespace object in my application is stored in global variable which is accessible everywhere in the application. :)
Other problem is that CommonJS standard forces specific syntax.
Other things are pretty much the same.
To translate from namespace syntax to CommonJS syntax I had to do two things. First was to use regular expression to replace all this kind of patterns:
[quote]Ns.components.ui.ComponentName[/quote]
to
[quote]exports.ComponentName[/quote]
Second, I had to modify my "Ns.require" method so it does not use "Ti.include", but "require" instead. The trick I used to do that coincidentally was basic idea that I used to solve problem of global variables. :)
"Ti.include" basically copy-pastes source code, while "require" returns what module exports. The trick here is to simulate what "Ti.include" was doing by using "require" function. That is, I just add module exports to global namespace object. The only thing that needed to be fixed was to "require" this global object on top of each module's file:
And that was it.
That is still not real CommonJS approach, but the thing here was following: on Android, "Ti.include" did not work inside functions (while "require" did when it was introduced), so everything had to be preloaded in the application (there was no way to include file on demand). For large project this was a disaster, I had to wait 10 seconds just to start application. Add 10 seconds of compilation time to that and you'll understand the secret of my high position in Appcelerator's Q/A. :)
I still don't use CommonJS in real sense, but I am slowly refactoring my application. For example, now I don't store configuration objects in global object, but in module's private variable.
[b]Final words[/b]
Although this approach is very nice in many cases, I found it limited in some ways. I miss some things in it, some things would be better if they were made in a different way.
Also, it's not the optimal. When building complex things for mobile devices, it is important to be aware of performance of your code from the beginning. Optimizing server is easy, you just buy more/better hardware. But you can't do that on mobile devices and with application size increasing, micro-optimizations are more and more noticeable (you will never, and not even then, do micro-optimization of server code).
After one year of programming with Titanium I have found two things.
To know how to program with Titanium, you must know JavaScript that is defined by ECMAScript specification (forget your jQuery skills). Basic JS skills are the best investment in your knowledge of Titanium.
After one year of intensive JS programming I have found that I don't know to think in JS (good thing is that now I am aware of that).
[b]Why to structure application[/b]
Well, you could put all source code files in same (root) application directory and everything would work fine. The thing here is ... when application grows, it becomes difficult to find particular file or to know where particular part of application is located if files in it are not organized in some meaningful way (for initial application developer, but also for all developers that will work on application at some time).
That's human nature. If you imagine large number of books, finding particular one can be difficult if they are not organized in any meaningful way. Search for particular book can be accelerated if books were sorted alphabetically. Human will search from left to right for first letter first and then for particular book. If books were sorted vertically in rows by first letter, human could find particular one even more faster because letters are clearly separated.
So, organizing things can be beneficial and the way they are organized is important.
That's why good application structure can be very useful. And not only application structure, but conventions in application also.
[b]Why "NOT to"[/b]
The answer is simple. Latest versions of Titanium framework introduced CommonJS standard that brought some new functionality, while changed old. That's why this way of structuring application is no longer good (and won't work in future SDK versions), but similar principles can be translated to new, CommonJS, functionality (and I'll show how).
[b]Basic concepts in my library[/b]
To understand application structure that I use, I need to explain some basic concepts that I use. This approach might not suit your needs, but it might give you some directions or ideas how to build your library for Titanium framework or how to structure your own application.
I'm using wrapped Object object as basic component. That component is basically container that has methods like add, remove, get, removeAll and getAll.
var component = Component();
var view = View();
component.add('MyView', view);
component.get('MyView'); //view
View is special component that is wrapper around Titanium UI View component. I wrote wrapper for every Titanium UI component (so, I don't use generic wrapper). Every wrapper can do everything what basic component can do, but beside that it proxies Titanium functionality (for example it adds or removes Titanium UI components in the background) and alters Titanium provided functionality where necessary (workarounds, simpler or enhanced usage, ...).
A good thing about this approach is that you can use this wrappers with native JS functionality without a fear that something will brake because they are native JS objects and not Titanium host objects. Titanium host objects in many cases do not behave like native JS objects and it's dangerous to do anything with them that is not documented. Also, this wrappers provide secure layer in cases when Titanium API changes or brakes (which is not so rare case). If you write code that depends directly on Titanium API, you could eventually end up in rewriting a lot of code. For example, "children" property in recent version was removed from some components. It is not so rare case that we need to get all child components of some component. If you depend directly on Titanium API, you would need to rewrite every part of your application that uses that property.
To simplify functionality of getting particular component, I add its reference as a property.
component.MyView // same as component.get('MyView')
I use string names to differentiate component instances of same type because it is easier to remember string component name, than integer index position in array of child components. Also, code that uses child component is not dependent on position in parent component so position can be changed without consequences.
I make component for everything that can be reused:
function Toolbar(config) {
var view = View({ bottom: 0, height: '10%' });
return view;
}
...
var toolbar = Toolbar();
toolbar.add('SaveButton', Button({ title: 'Save' });
toolbar.add('CancelButton', Button({ title: 'Cancel' });
"Toolbar" can again become some kind of component (everything is a component):
function ActionBar() {
var toolbar = new Toolbar();
toolbar.add('SaveButton', Button({ title: 'Save' });
toolbar.add('CancelButton', Button({ title: 'Cancel' });
return toolbar;
}
This approach is very similar to the one that can be found in EnyoJs (http://enyojs.com/). So, basic idea is to build small components first and then use them to build more and more complex components until whole UI is built. This approach has showed to be very flexible and scalable. Also, once all low level components are built, building higher level components is easy task.
I implemented some things that Titanium framework lacks, like FormView and GridView. FormView is awesome component, basically, every component it contains and that implements "setValue" or "getValue" method FormView can simply use to set its value and to get its value. That way I made that component very generic and it can use literally anything that somehow implements that methods (for example, I use table view in forms, table view is actually collection of data that can be dynamically added or removed). FormView component also implement that methods, so they can be nested.
function Login() {
var formView = FormView();
formView.add('Content', View());
formView.add('Toolbar', ActionBar());
var textField = TextField({ value: 'iskugor' }); //implements "getValue" and "setValue"
formView.Content.add('UserName', textField).
formView.getValue(); // returns { 'UserName': 'iskugor' }
formView.setValue({ 'UserName': 'anonymous' }); //sets text field's value
return formView;
}
Basic application would look like this:
//very simplified
var win = Context();
var form = Login();
win.add('Form', form);
win.open();
Context is window abstraction. It is different from all other components because it is important for memory management. Before 1.8 and especially before 1.7 SDK, Android part of Titanium was unstable and building something complex was mission impossible. Tom Cruise in me made this Context window abstraction that would remove all components and their events when window would close. From what it seemed to me at that time, in some cases, some event listeners were not removed so after window was re-created and re-opened, application would crash. By removing events manually, application would not crash. Also, at that time, many people had memory leak problem that sometimes just could not be solved even with some workarounds that came from community (workarounds that I tried did not work for me). By removing components manually, I did not have memory problems. From what my tests shows, 1.8 has much better memory and event handling and I actually don't use this features any more.
Removing components is easy task when there are wrappers that have "getAll" method, but what about listeners? In Titanium, event system is very basic and don't have much features. Also, Titanium component's "listeners" property was the only way to get list of all listeners, but it was unfortunately removed (partially, at least).
That's why I implemented my own publish/subscribe pattern.
Events.subscribe(button, 'click', function(e) {
//do something
});
Internally, Events is a container that can register events, publish events, remove (all) events, remove events by particular event type and so on.
Events.subscribe(button, 'CustomEvent', function(e) {
Ti.alert('Custom event!');
});
Events.publish(button, 'CustomEvent');
Events.removeByEventName(button, 'click'); //removes all click events
Events.removeAll(button); //removes all events
In the background, I don't use "listeners" property, but my own container, similar to the one I use for basic component. Events are registered by event type and because of that it is easy to remove all events associated with particular event type. Also, to remove events by some event type, there is no need to store anonymous function anywhere (it is stored internally in "Events" container).
If first argument is omitted, app-level event is created. App-level events are dangerous to use because they they remain in memory all the time application runs and they can potentially create memory leaks, so functionality described above is great to have in cases then app-level events are used (all app-level events can be removed with one statement).
With all this functionality, removing events is trivial task.
Because I don't manually remove events when context closes anymore (starting from 1.8), some extra measures are needed to be done in this implementation to prevent memory leaks. I found that this event system is limited (and IMHO it forces some bad practices) and I have a design idea for new, more advanced, event system that would suit better to usage with my library.
[b]Namespaces and naming convention[/b]
To easily name and find components, I split components to different namespaces. I have one root namespace that has subnamespaces.
Ns = {}; //root namespace
Ns.components = {}; //components namespace
Ns.components.ui = {}; //components ui namespace
Ns.components.filesystem = {}; // components filesystem namespace
Ns.events = {}; // events namespace
Concrete implementation of some component would be added to particular namespace where it belongs:
Ns.components.ui.Toolbar = function() {
...
};
Component name, as every other function constructor, starts with capital letter, while namespace names are lower case.
I made convention that component's code is located in one file and that component namespace name is mapped to file path relative to "Resources" directory and file name. For example Toolbar component's code would be stored in toolbar.js file. Since Toolbar is in "components.ui namespace", toolbar.js file would be located in "Resources/components/ui" directory (FormView component's file name would be form_view.js).
I never put concrete, instantiated component to namespace, because that's a bad practice that might work on iOS, but brakes on Android. Using one component, or even more, adding one component to different windows (even if that doesn't happen in the same time) can have strange side effects. The best approach is to create twin-component whenever it is needed by using function constructor.
To load particular component, I use custom loader:
Ns.require('Ns.components.ui.Toolbar');
That method first checks if component is already present in the namespace, if not it includes it:
Ti. include('/components/ui/toolbar.js');
Every component can have default preferences, which I store to global configuration object (although, code is located in component's file):
Ns.config.add('Toolbar', {
height: '10%',
bottom: 0,
layout: 'horizontal'
});
which is usually merged with configuration object passed to component's function constructor:
Ns.components.ui.Toolbar = function(configuration) {
var config = Ns.config.merge(configuration, Ns.config.get('Toolbar'));
var component = View(config);
return component;
};
That way other components can change default behavior of some component by passing configuration object modifications as function parameter.
[b]MVC[/b]
To separate concerns and to make simple implementation of MVC pattern, I separated how components are defined in the interface and how do they behave (this is actually very useful when one component is used in different situations and therefore they have modified behavior but same appearance).
To do this, I enhanced some Titanium wrappers so they can automatically add other components through configuration object. That is done by convention, configuration object can have special "items" property which defines components that will be automatically added:
var actionBar = Ns.components.ui.View({
height: '10%',
bottom: 0,
layout: 'horizontal',
items: {
'SaveButton': function() {
return Ns.components.ui.Button({ title: 'Save' });
},
'CancelButton': function() {
return Ns.components.ui.Button({ title: 'Cancel' });
}
}
});
actionBar.SaveButton; //button
This basically is used for "View" part of MVC.
"View" is defined through configuration object defined for every component.
For example, "View" part of ActionBar component would be:
Ns.config.add('ActionBar': {
height: '10%',
bottom: 0,
layout: 'horizontal',
items: {
SaveButton: function() {
return Ns.components.ui.Button({ title: 'Save' });
},
CancelButton: function() {
return Ns.components.ui.Button({ title: 'Cancel' });
}
}
});
Ns.components.ui.ActionBar = function(configuration) {
var config = Ns.config.merge(configuration, Ns.config.get('ActionBar'));
return Ns.components.ui.Toolbar(config);
};
To make Controller part of MVC, View part has to do something. I defined that before in when I described "Events":
Events.subscribe(form.Toolbar.SaveButton, 'click', function(e) {
Ti.alert('Username: ' + form.getValue().UserName);
//do something with login data
});
Besides interaction, Controller is responsible for component construction, parameter passing, gluing components together, passing data to View, getting data for View and so on. Controller is what gives life to View, View is stupid as it can be (it's just definition through configuration object).
"Model" part is up to you. :)
I use modified version of Joli ORM (https://github.com/xavierlacot/joli.js/) and Sqlite for database models.
[b]Application structure[/b]
Application structure has three main parts: library, components and modules.
In library, there are all common things, like functionality for including components, creating namespaces, events system definition, database ORM and so on.
In components there are all general components that have no specific data in them. That components can be reused in multiple projects. For example, there are UI components (Button, Toolbar, FormView), filesystem component, XML component, ...
[quote]/components/ui/button.js[/quote]
In modules there are all project-specific components with data in them grouped into modules and their submodules where necessary (for big modules it's handy to group them into submodules). This components are general components with specific data in them. For example, if Button is general component, specific component would be LoginButton which is Button component with specific title.
[quote]/modules/user/ui/login_button.js[/quote]
View part is stored in "ui" namespace, while Controller is stored in module's root directory. Beside that two, I store models in module's directory also.
[quote]/modules/user/models/db/users.js[/quote]
Common practice that many people follow is not to separate this two kinds of components (with and without specific data), but I think it is better to separate them. If you mix project specific data with general components, then it will not be possible to reuse them in future projects. By separating them clearly, you can just copy components to new project (or link common library when that functionality become available in TiStudio) and start working on project specific modules.
[b]Translation to CommonJS[/b]
I'll show you how I easily translated to CommonJS standard from this namespace-based application structure.
The biggest problem in CommonJS implementation in Titanium framework is that it does not have global variable implementation. As you might have noticed, namespace object in my application is stored in global variable which is accessible everywhere in the application. :)
Other problem is that CommonJS standard forces specific syntax.
exports.ModuleName = function() {};
//or
module.exports = {};
Other things are pretty much the same.
To translate from namespace syntax to CommonJS syntax I had to do two things. First was to use regular expression to replace all this kind of patterns:
[quote]Ns.components.ui.ComponentName[/quote]
to
[quote]exports.ComponentName[/quote]
Second, I had to modify my "Ns.require" method so it does not use "Ti.include", but "require" instead. The trick I used to do that coincidentally was basic idea that I used to solve problem of global variables. :)
"Ti.include" basically copy-pastes source code, while "require" returns what module exports. The trick here is to simulate what "Ti.include" was doing by using "require" function. That is, I just add module exports to global namespace object. The only thing that needed to be fixed was to "require" this global object on top of each module's file:
Ns = require('library/ns');
And that was it.
That is still not real CommonJS approach, but the thing here was following: on Android, "Ti.include" did not work inside functions (while "require" did when it was introduced), so everything had to be preloaded in the application (there was no way to include file on demand). For large project this was a disaster, I had to wait 10 seconds just to start application. Add 10 seconds of compilation time to that and you'll understand the secret of my high position in Appcelerator's Q/A. :)
I still don't use CommonJS in real sense, but I am slowly refactoring my application. For example, now I don't store configuration objects in global object, but in module's private variable.
var defaultConf = {
...
};
exports.ComponentConstructor = function(configuration) {
//merge
};
[b]Final words[/b]
Although this approach is very nice in many cases, I found it limited in some ways. I miss some things in it, some things would be better if they were made in a different way.
Also, it's not the optimal. When building complex things for mobile devices, it is important to be aware of performance of your code from the beginning. Optimizing server is easy, you just buy more/better hardware. But you can't do that on mobile devices and with application size increasing, micro-optimizations are more and more noticeable (you will never, and not even then, do micro-optimization of server code).
After one year of programming with Titanium I have found two things.
To know how to program with Titanium, you must know JavaScript that is defined by ECMAScript specification (forget your jQuery skills). Basic JS skills are the best investment in your knowledge of Titanium.
After one year of intensive JS programming I have found that I don't know to think in JS (good thing is that now I am aware of that).