Titanium application structure – learning from Tweetanium

Struct - learning from Tweetanium regarding application structure best practices!

Up until recently, like so many other lost souls, we've followed the same Titanium application structure as Appcelerator's KitchenSink application. In this paradigm, when you want to open a new window, you'd typically do something like this:

Inside somefile.js the window would be populated with controls, using the Ti.UI.currentWindow reference. We can access the data from the previous window through win.importantdata.

This approach, even when improved upon, has several disadvantages:

  • If you have a file with application helper functions, you need to import that in every other file that needs the functionality. In my Tristania app for example, pretty much every file starts with Ti.include(“../assets/utils.js”).
  • Every time a window is meant to be created, the relevant file must be read and code parsed.
  • The structure of the app becomes rather opaque.
  • It is very difficult to unittest this approach.

It all works, sure, but I've never been comfortable with my application structures, feeling there must be a better way. Even though there are (many) ways to improve on the above example, I wanted a different structure paradigm alltogether. Alas, I was too lazy to get around to experiment with it, so “it remained but a beautiful thought”, as we say in Sweden.

Fortunately, Kevin Whinnery, Chad Auld and the rest of the Appcelerator team have no such character flaws! Recently they opensourced Tweetanium, a fully functioning Twitter client coded with best practices regarding both JavaScript in general and Titanium in particular.

Which was like christmas to me! The only non-Krawaller Titanium source code I've gleaned (with a few none-noteworthy exceptions) is the aforementioned KitchenSink, which – let's be honest – is not a great piece of work at all. It demonstrates the API very well (which is great since the docs are crap), but as an application example, it leaves me wanting.

So, enter Tweetanium. As soon as you open up the sourcecode, it is evident that they're doing things very differently! I have created a stripped-down, barebones version of it called Struct, aiming to expose the new structure paradigm. You'll find the git repo here. This is what it looks like – isn't knowledge beautiful? :)

(download)

The main point in the Tweetanium approach is that my various files (like somefile.js in the above example) are not messing with Ti.UI.currentWindow – instead, it augments a global namespace with a constructor variable. In Struct, I end up with the following global object:

The program flow is that the otherwise pretty empty app.js imports struct.js, which can be considered your main app definition file. This file creates the global namespace and maybe some utility functions, and then includes the other files to populate the rest of the namespace.

This means that as soon as the struct.js file is included, the entire app structure is created in memory. No more parsing of files will be done during the session.

App.js will then call the createApplicationWindow function, call the open method on the returned window, and maybe do some other app initialization stuff. Now the app is up and running! In Struct, app.js looks like this:

So what are the great advantages to this approach? I've already found quite a few, but it boils down to a single keyword: closure. Every single function is executed in the same closure (ok, maybe subclosures, but you get the gist). Read that sentence again until the implications set in.

This is good news from a performance perspective (and most likely stability) – no more importing the same file a gazillion times! But from an infrastructure point of view its even yummier:

  • As we always have access to the same scope, we can keep app-wide data in a single object, instead of JSON:ing back and forth with Ti.App.Properties.
  • All kinds of other convenience structures are suddenly very easy to build. In Struct there is a global messaging system, using one single view, instantiated only once. Tweetanium does a similar thing with a loader view, and has a very neat Model baseclass thing going.
  • You can instantiate all app windows and views immediately, as they all live in the same closure. Windows opened at a later point will also live in the same closure, since that's where the constructor function is defined.
  • Creating your own navigation system ("immersive UI", to use Apple's lingo) is a breeze. In Struct I've stolen the home-brewed tabs from Tweetanium.

Now, having autopsied Tweetanium, created Struct and looking at the layout before me, it seems very obvious and self-evident. Perhaps it already was to the rest of you, but for me it was a new-found revelation, and I can't wait to test it out in our next project! So, for those of you who like me hadn't caught on to this train before, check out Struct & Tweetanium and try it out!

Posted by David Waller 

41 comments

Feb 18, 2011
Aitor said...
Thanks for this, it helped me a lot (I was waiting the right time to dive in Tweetanium and Struct is so easy to follow).
Be aware of the non-declared variable animationDuration in "ui.js". It throws an error when tries to animate the tab (applicationwindow, line 90)

Thanks again

Feb 18, 2011
David Waller said...
Right you are, good catch! Seems I was in too much of a hurry, ripping out the Tweetanium stuff. Strange, it didn't throw an error for me, which is why i missed it.

Fixed now in the repo. Thanx again!

Feb 26, 2011
Sam said...
David, thank you so much for this clarification of what was going on with Tweetanium project.
I was trying to understand the whole thing. But it was too much to follow! This is so kind of you to clear it out for noobies like me!
Also, I couldn't make the configuration for tweetanium to work with oAuth thing! I have no idea how to do that which makes it even harder for me to follow the design pattern that tweetanium did!
Thank you so much. I will add you in twitter iSam_sa. I want to ask you a question about tweetanium!

Regards!
Sam

Feb 27, 2011
David Waller said...
My pleasure, glad you found my ramblings useful! It took me some time to untangle what was going on, but stripping it all down to Struct was a good way to wrap my brain around it. Happy you feel the same way!

Any questions just ask away!

Mar 01, 2011
Sam said...
Hey David, thank you for your kind comment! You're right Struct is a better way to understand what was going on in Tweetanium. However, there are still some variables in Struct aren't actually used. like $$.stretch variable ... etc! but seriously, you've done great job that made me want to look deeply in tweetanium after I understood the hang of it. I was not able to get tweetanium running! the configuration file thing! I mean when I login it says authentication failed! Can you help me?
Thank you so much
Mar 08, 2011
Nawara said...
Sam, I think your problem with twitter authentication is the fact that Tweetanium uses the xAuth authentication method of twitter. To use xAuth you need to get approval from Twitter first. Otherwise you can just use oAuth. Drop them an email and let them know you want to use xAuth, they usually respond within a couple of days max!

Good luck

Mar 08, 2011
Nawara said...
David,

Seriously man awesome job! I spent hours looking at the tweetanium code trying to understand what on earth was going on with no success. Thanks for stripping it down to the core and making it so easy to grasp.

cheers

May 12, 2011
Matt said...
I am very lost trying to understand mixin and combine. Any suggestions?
Thanks for hard work!
May 20, 2011
Tom Raggett said...
@David: Very useful: thanks for the work in stripping out the Tweetanium app stuff, especially while waiting for xAuth OK from Twitter. Thanks.

@Matt: as I read them, mixin and combine are clever ways of managing and overrriding default properties for object. Using mixin, you can change the e.g. one thing you need this time round and pull in the other default properties for the object you are using. Some info here http://docs.dojocampus.org/dojo/mixin

May 23, 2011
Mikko said...
Thanks for sharing this! I'm just wondering how many views you can create for one window in terms of memory consumption? That is, should I still try to split my application to few sections (windows), which contain few views? Do you have any experience on this?
Jun 03, 2011
asosso said...
Anyone getting rotation bugs on iPad? If you go to landscape, you can see the tab to the right. Any fixes?
Jun 21, 2011
Toonsend said...
Thanks for taking the time to share this.
Jun 24, 2011
Christopher said...
Great, great work -- This resource should be included in the main distro, imo -- Love it.
Jul 01, 2011
Grayson said...
Is it possible to use the regular tab group instead of these custom ones? Thanks a bunch :)
Jul 01, 2011
David Waller said...
@Grayson: Oh, most definitely! The only reason for using the custom tabs here is that it is a prime example of something that would be impossible to do (well, at least neatly) using the old way. But there is certainly nothing stopping you from using real tabs even when laying out the code of your app according to the thinking in Struct/Tweetanium.
Jul 02, 2011
Grayson said...
@David: But what would that look like? Could you drop a Gist? This approach looks geared toward a master window, and I'm not quite sure the best way to reshape for a more "native ui" approach.
Jul 03, 2011
David Waller said...
@Grayson: It looks pretty much the same, but you'd have a master tab group instead of a master window. Also, foo, bar and baz will be created as windows, instead of views, to be used in the tabs. But the main advantage remains, namely that all windows are created in the same closure. I quickly threw this together in a new Struct branch, so you can see it in action: https://github.com/krawaller/Struct/tree/nativetabs
Jul 13, 2011
J Alammar said...
David, thank you so very much for this post. It explained things beautifully.

If we're ever in the same city, I'm buying you lunch.

Jul 14, 2011
levi730 said...
Thank you for this! It looks like a much nicer way to structure my apps. The last thing I'm having trouble wrapping my head around is how to use the regular navigation. I'm working from the nativetabs tree. Let's say I wanted the foo window to use the regular navigation (where one "window" goes to another and a back button is provided). How would that work? I tried adding a S.ui.createsubwin function, then calling that, but it opens outside of the tab structure entirely. I'm sure it's probably something really basic, just pointing me to the right section of the docs would be much appreciated.

https://gist.github.com/1084006

Thanks!

Jul 14, 2011
David Waller said...
When you call the open method on a window, what you describe - opening outside the current tab group - is the correct behaviour. What you want - having the window open within the current tab - is achieved by sending the window object to the open method of the current tab.

And, since we stored the mainTabGroup in S.app (I've since started calling it S.state, which makes more sense), we have direct access to the current tab from within foowin.js! All we need is to change this:

subwin.open();

...to this:

S.app.mainTabGroup.activeTab.open(subwin);

See new fork of your Gist here: https://gist.github.com/1084092/

Jul 14, 2011
levi730 said...
Ah! Sweet!

I managed to skip some of the goodness held within S. There's a big mental shift from the documented structure of opening all those windows by URL, this helps a LOT! Thank you so much!

Jul 16, 2011
Joey said...
Is there an easy way to utilize Struct and Livetanium? I'm trying to use both but server.js doesn't seem to be watching files within /struct/ui/*. Any help will be appreciated.
Jul 20, 2011
PacoL said...
Hi! I'm developing an app with the tabgroup structure that help me a lot but I don't know if is possible create a general messaging system like the first example or I must make a message view for every window inside the tabgroup? Any help will be apreciated.
Thank you so much
Jul 21, 2011
David Waller said...
@PacoL: Sorry, I didn't even realise I ripped out the message system when I went native with the tabs. Restored now in the nativetab branch, so just pull again!

A TabGroup is a view, so you can add view children to it as you usually would. These will live "above" the tabs, and aren't affected when you switch between the tabs.

Jul 26, 2011
PacoL said...
Hi David! thank you so much for your answer. Mi first aproach was add the message view to the tabgroup but isn't working. I´m going to download your example and prove it.
Jul 26, 2011
PacoL said...
Hi again. I can't do the message works. I don't know why, everything seems right but the message is not showing.
Jul 26, 2011
Nick said...
Hey David,

Great post! This has really helped me understand the tweetanium structure which is better than the kitchen sink approach i used to build my app. I have a follow up question though - I understand the whole global context approach but have a major concern - Memory Consumption.

I have several heavy weight windows in this app (think of kitchen sink) and using a global context approach loads everything into memory. So basically once a window is opened the allocated memory is held forever and not all of it is released even when the window is closed. So I was hoping you could give me a few tips on how to tackle this issue. I'm using the approach you mentioned here:
https://github.com/krawaller/Struct/blob/nativetabs/Resources/struct/ui/barwi...

I've been struggling with this for awhile so any pointers would be greatly appreciated.Thanks

Jul 27, 2011
PacoL said...
Finally I try a diferent aproach with my message's problem. In the event listener open and close the message view in the active window.
Jul 30, 2011
David Waller said...
@PacoL: That would work, I guess, but it is strange that the first approach didn't work! Are you on Android? I only tested all this on iOS, so maybe some obscure bug slid past me.

@Nick: I must admit that memory consumption in Titanium apps isn't one of my strengths, although I share your concern and plan to immerse myself in the subject at some point in the future. However, the single-closure structure in and of itself really shouldn't be any different from the KitcchenSink approach in regards to memory!

Global context doesn't mean you HAVE to have lots of window instances at the same time. It just means you CAN. You can also choose to instanciate them only when needed and then close them again, much like KitchenSink does. The only difference is that the CONSTRUCTORS are already defined, and live in a single closure. This shouldn't be a significant memory-overhead.

In my (admittedly limited) experience, having lots of window instances simultaneously is pretty cheap. In our latest app, I instanciate every single window within the app at startup, and then just switch between them as the user navigates around. Not sure if this actually is better than to just instanciate the current window and then close it again, but it definitely seems to flow smoothly.

Are you sure you're seeing a difference memory-wise when comparing the two methods? I know that Titanium is generally not too efficient with regards to memory, and that the cleanups aren't doing all one could wish from them.

Aug 01, 2011
PacoL said...
Hi David!. I`m testing on Android but this weekend I´m going to try on iOS.
Aug 03, 2011
Nick said...
Hey David,

Thanks for your reply. I must confess that I have not implemented the whole app with the new single closure approach but simply replicated basic functionality in 2 windows and hence don't have a true comparison between the 2 methods. I presumed the whole global context might have severe effect on the memory consumption. I understand titanium has it's limitation and I guess sometimes you just have to take the good with the bad.

I'm intrigued by your approach of initializing windows on startup. That should definitely give the app a smoother outlook. Do you init all sub windows as well or is that done on the fly? I'm personally struggling with the fact that my heavy weight sub windows consume 'x' amount of memory when opened and not releasing all of it.

However, your post has certainly given me some ideas on how I might be able to tackle my problem ( i guess talking it out helps). Will def post my successes or failures if you're interested. Once again appreciate your reply. Btw, if your app is available on the app store do let us know, would love to check it out.

Cheers mate.

Aug 06, 2011
Paul said...
I just wanted to say thanks very much for putting this together. I was a bit hesitant to go with Appcelerator route, but decided to dive in yesterday. Within just a matter of hours, I already had the first screen developed and fully working with remote data. The only problem that I came up with was the possibility of having everything in procedure code.

But this has been a real eye opener and if it helps with stability and code I'm much happier!

So thanks again!

Aug 06, 2011
Paul said...
Quick question for you, here is a pastie: http://pastie.org/2331489

Is there a way of modifying the label which is created by the createpulldown from the event listener in createtableview ?

I know I could use global variables, but I'd rather everything stay local if I can.

Aug 07, 2011
David Waller said...
@PacoL: I've since learned (from our TiView experiment, see new blogpost) that adding "normal" view children to TabGroups (and other non-parent-intended controls such as switches or sliders) is very unreliable in Android. I'll have to think of some new solution for the message system (probably using a modal window instead).

@Nick: I init all subwindows too in my app. Still not sure whether or not this is a good idea, but definitely works! Would love to hear how you fare, so by all means, give a shout when you have war stories to share!

@Paul: Glad it was of help! Regarding your question, it can be solved by saving a reference to the label on the returned object from the createpulldown constructor. Here's a new pastie: http://pastie.org/2333857 (not tested, so pls don't sue me if I screwed sth up)

Aug 07, 2011
Paul said...
@David. Thank you for that pastie. The reference was definitely what I needed.
Aug 09, 2011
thank you so much for this posting. I'm a newbie from half way around the world and your blog article helped me a lot to sort things out. my question might not be related to this but, i'd like to ask one thing. im trying to build an app on top of your nativeTab framework. it has 4 tabs on the bottom and, need to open a modal window for sign-in/sign-up.

the sign-in/sign-up window doesn't belong to any of 4 tabs. in this senario, what is the best practice to create and open the independent modal window? i can create and open a modal window in a kitchen sink way. (here's what i did: http://pastie.org/2343917) but, i wish i can use your approach. Please enlighten me a little bit.

Sep 02, 2011
Peter said...
Thank you for your explanation, really clears up some things!
I tried debugging this on Android, but I get the following error:
TypeError: Cannot find function createApplicationWindow in object [object Object].

Any ideas on this?

Thanks in regards!

Sep 03, 2011
Peter said...
Found it, at least I think. The app compiles for Android if I move Ti.include("/struct/ui/applicationwindow.js"); from ui.js to struct.js. Maybe this is an recursion problem.
Sep 19, 2011
JoshuaIRL said...
I know this is kind of dated but I'm just now seeing it...
What about Android's Hard-Key arrows for navigating elements for non-touch devices.
With a normal Tab Group it can select the tab button and press center key but using your tabs it's not going to work.

What do we do to combat this?

Oct 13, 2011
Antho said...
Hi all, I get exactly the same issue as Peter on Android : TypeError: Cannot find function createApplicationWindow in object [object Object] ! With a "too deep recursion while parsing error" on the simulator.
This code works well on the ipad but crashes on my Asus tablet.. :s
Someone got an idea?

Super work by the way !!

Dec 11, 2011
Viet said...
Thank you for your great post. It really helped. I am currently using it to develop an app for a non-profit. We love the fact that we can customize our tabs. The only problem is after getting it to work on Android, it no long works on iOS. Also, we aren't sure how to open windows within the views themselves.

I noticed that tweetanium uses views rather than separate windows. These views are dependent on a a film strip? Does this prevent imageview from loading on iOS? Maps? I've read around that tweetanium as a structure is very limitied. Have you had any experience with this.

Leave a comment...