Twitter LinkedIn E-mail RSS
Home Development Appcelerator Titanium commonJS & Global Variables
formats

Appcelerator Titanium commonJS & Global Variables

appceleratorAppcelerator have released a new version of their Titanium mobile development framework SDK (1.8.0.1),  and there are some major changes taking place. I will say here that although these have caused me a lot of extra work these changes are a good thing, and going forwards will make coding even more fun.

If you have been keeping track of Titanium version 1.8.0 RC3 you wil be aware of the major change in moving from a global namespace architecture to commonJS. You may not be aware of the implications this raises.

The major issue I hit when refactoring a 10000+ line code base from global namespace to commonJS, was that in commonJS there is no global space. Although you can declare an application level variable that is really not good practice. I had to have global variables which were static and maintainable or I would have to completely rewrite my app.

A few discussion with people at Appcelerator ensued around this topic, and a big thanks goes out to Matt Apperson, Paul Dowsett and Kevin Whinnery for there help and guidance in solving these issues.

So to start. To obtain a global variable you need to create a commonJS module, this is a very simple architecture and you can create as many as you require to keep your code base abstracted out into a good architecture.

I created a file in a sub directory ‘common’ called globals.js

// My common global variables.

exports.GV  =
{
    variable1:   'This is a static variable',
    variable2:   'This is another static variable',
    variable3:   'http://www.thewarpedcoder.net'
};

exports.GVUpdate   = function(inValue, inName)
{
    this.GV[inName]    =     inValue;
};

The following code snippet shows how to use this within any other module, in this case I used a file I created “control.js” in a sub directory control.

var VARS  = require('/common/globals);

alert(VARS.GV.variable1);
alert(VARS.GV.variable3);

VARS.GVUpdate('www.thewarpedcoder.net', 'variable3');

alert(VARS.GV.variable3);

The exports.GV is fairly obvious this just makes the objects available. The exports.GVUpdate enables the values to be updated. When including these files in any other file (module) then the variables with the new values are available.

Appcelerator TitanA gotcha which may be a bug, If you update the value in a sub module and then fire an event into a parent module, the value in the parent module does not get updated. So until this is either established as a bug and fixed or deemed the way it works, try and always update the values in the parent module. (I hope this makes sense!!!!).

UPDATE after looking into this further the above GOTCHA is not a bug, it works as it should, the issue I had was with a specific variable which wasn’t updating due to another completely independent issue.

Well thats it for now. Hopefully this will help a few people resolve the global space issue when refactoring their code.

some useful links……

Forging Titanium commonJS Modules

Titanium Best Practices

commonJS

 

 
 Share on Facebook Share on Twitter Share on Reddit Share on LinkedIn
10 Comments  comments 

10 Responses

  1. Hi Trevor, thanks for sharing. I’m still considering the best way to work with commonJS modules in Titanium. By now, I only use them for reusable components and I use the standard global namespace like tweetanium. Your post offers a new vision, I like it, but I’m still a bit confuse about how to share info between isolated modules.

    One more question, have you tested both on android and ios? Any comment about platform differences?

    NOTE: in the first code block, I think the line “exports.GVUpdate = function(inValue, inName);” should not end with a semicolon.

    • thewarpedcoder

      thanks for the note… Fixed post.

    • thewarpedcoder

      Yes this works on both IOS and Android, also next blog post will deal with commonJS architecture, and this method does pass values between the modules.

  2. Hi Trevor,
    I’m not convinced when you say: “in commonJS there is no global space”.
    Maybe I didn’t understand well, but why should you put in place such kind of mechanism involving a module containing global state and require it in every other module that needs it?
    There is no big difference between using “require()” vs. “Ti.include()”, except that CommonJS enforces a standard way for creating modules. In particular, the code in a JS module, sees the global scope in the state it was just before it’s loaded through require().
    So given this simple module called module_a.js:

    Ti.API.info(‘module_a’);
    exports.modify_a = function(e) {
    a = e;
    };

    called by this code in app.js, where the a global variable is created:

    var a = ‘global A’;
    Ti.API.info(‘a = ‘ + a);
    var mod_a = require(‘module_a’);
    mod_a.modify_a(‘modified by me’);
    Ti.API.info(‘now a = ‘ + a);

    produces this output:

    [INFO] a = global A
    [INFO] module_a
    [INFO] now a = modified by me

    In other words, in your case, at first, you could simply instantiate your GV module once before any other (even without the need of the GVupdate function), without needing to require it in every other module. However with some more refactoring you would probably get rid of the dependencies from the global vars from your modules (but that’s another story, I know – I’m facing similar problems related to heavy interdependencies between modules in a project of mine).
    Am I missing something?
    Thanks,

    Olivier

    • thewarpedcoder

      Hi Olivier

      Thank you for such a well thought out response.

      In answer to your question, yes you are missing something. In that if you then required another file and needed access to that variable in it, it is not available, without firing an event to go back up to app.js and retrieve the value.

      Keeping everything in app.js gives you a sort of global scope but in a more complex example it would hit issues. Also currently within Titanium the global scope does exist in IOS but this is considered a bug and is being removed. I spent a lot of time resolving this with people at Appcelerator as I really didn’t want to rewrite the whole architecture of my app. But in reality there should be no global variables even the way I have done it.

      Hope this explains it a little more.

  3. Hi Trevor,
    thanks for your response. I now see what you meant. Indeed the Appcelerator guide on CommonJS states: “There shall not be ANY global variables in a Titanium application shared across all modules.”, though I see this more as a best practice than a mandatory rule.
    I’ll probably need to better investigate this kind of issues.
    Cheers,

    Olivier

    • thewarpedcoder

      Hi Olivier

      Actually I think going forwards this will not be best practice, but will be the only way. The current global scope will not exist neither will the option of Ti.include.

      So please consider this as the way forward, not just a suggested way forward, or you may hit bigger issues in the future.

      Hope this helps.

      T..

      • Sorry for insisting on this thread, though it’s not clear to me what “The current global scope will not exist” actually means.
        Do you have first hand info about Appcelerator going in the direction of making app.js a module itself with a well defined interface? Only in this case I can see the global scope to “disappear”. Or, better, to not be modifiable by user’s code, since there’s a bunch of objects that will need to live in the global scope, exactly as it happens now (e.g. the Titanium object, the require function, etc.).
        IMO, only telling “app.js is a commonJS module that must export a specific function used for bootstrapping the app” can enforce the global scope not to be polluted by user code in any way, since variables created in a common js module acquire a module scope.

        • thewarpedcoder

          Hi Olivier

          I think after the next week and appcelerators developer week of webcasts things will become a lot clearer. Although I have had communication directly with Appcelerator on this I do not have the inside track, but I do expect that the current global scope will not exist for much longer. There is a big difference between the framework objects and the global scope, the Titanium components and API’s will remain as will the require, but they are built into the framework and this post is about using the framework. The global scope I refer to is that of the code written using the framework. Hope this explains a little more, but follow the webcasts over the next week and see what comes out.

  4. Chad

    Can you clarify what you mean by ‘If you update the value in a sub module and then fire an event into a print module, the value in the parent module does not get updated.’

    In our app, we need to have some shared (static) state that can be used in multiple modules. I’ve been doing some experimenting, and so far what I’ve put together seems to be working.

    ## module1
    exports.variables { var1: 0 };
    exports.setVar1 = function(val) { this.variables[‘var1’] = val; };
    ## module2
    var mod1 = require(‘module1’);
    exports.setVar1 = function(val) { mod1.setVar1(val) };
    ## module3
    var mod2 = require(‘module2’);
    exports.setVar1 = function(val) { mod2.setVar1(val) };

    ## app.js
    var mod1 = require(‘module1’);
    var mod3 = require(‘module3’);
    mod1.setVar1(‘fail’);
    mod3.setVar1(‘win’);

    print(‘You ‘ + mod1.variables.var);

    In this situation, I get ‘You win’.