Home » Odeon Blogs »

Tudor, UI/UX Maestro

Lua vs. Obj-C

Lua

For some time now I’ve been digging into Lua. Coming from Python, the power of a clean syntax and good documentation is impossible to ignore.

Lua is a powerful, fast, lightweight, embeddable scripting language.


Lua combines simple procedural syntax with powerful data description constructs based on associative arrays and extensible semantics. Lua is dynamically typed, runs by interpreting bytecode for a register-based virtual machine, and has automatic memory management with incremental garbage collection, making it ideal for configuration, scripting, and rapid prototyping.

It’s that exotic, super-fast scripting language that very few use, right? Partially right. It’s extensively used in the gaming industry (e.g. World of Warcraft) usually together with C++.


Lua comes with a console, which is great tool for fast experimenting of logic and syntax.

Lua + iOS


spidey vs hulk

All fine and dandy, but how can this help me on iOS development? Stefan found the right answer: iPhone Wax .

Wax is a framework that lets you write native iPhone apps in Lua. It bridges Objective-C and Lua using the Objective-C runtime. With Wax, anything you can do in Objective-C is automatically available in Lua!”
Corey Johnson, main guy behind Wax

As anyone with iOS development experience knows, Obj-C is very strict about classes, requiring a certain mindset to write good, maintainable and modular applications. Wax doesn’t alter this approach at all.


Memory management is another worry that goes away in Wax, because of the automatic garbage collection.


By this point, Wax looked completely different than any other write-quick-iphone-apps gimicks out there, including PhoneGap, Titanium, Rhomobile, Corona SDK. Do note that I mention “iPhone apps”, since the result of Wax is not cross platform. Well, a solid application should take advatage of the hardware platform and the only part that can be cross platform is the interface, even that within certain limits.

Cocoa with a sprinkle of Lua

Wax looked good in theory. A UITabBarController is a UITabBarController, a delegate is a delegate, 3rd party libs work how they are intended to (Facebook, ASIHTTPRequest, custom written ones). Now let’s see it in practice.


I gave Wax a run-around for 1 week. During that time I tried using most of the UITouch, Cocoa elements and some 3rd party libraries. The results were good. The interface is just as responsive as building directly in Obj-C.


After that test week, the decision of using Wax for the rest of app development seemed obvious, even more, Wax seems like a valuable addition to the development cycle.

Side-by-side example and benchmark

Here’s a small benchmark of memory usage by the same application, written in Wax and in Obj-C. The application used is Fortune Crunch from Mark’s tutorial on MobileTuts+.

Objective-C code (can be seen in the source code of the tutorial too):

  1. // FortuneCrunchAppDelegate.h
  2. //
  3. #import <UIKit/UIKit.h>

  4. @class FortuneCrunchViewController;

  5. @interface FortuneCrunchAppDelegate : NSObject <UIApplicationDelegate> {
  6. UIWindow *window;
  7. FortuneCrunchViewController *viewController;
  8. }

  9. @property (nonatomic, retain) IBOutlet UIWindow *window;
  10. @property (nonatomic, retain) IBOutlet FortuneCrunchViewController *viewController;

  11. @end



  12. // FortuneCrunchAppDelegate.m
  13. //
  14. #import "FortuneCrunchAppDelegate.h"
  15. #import "FortuneCrunchViewController.h"

  16. @implementation FortuneCrunchAppDelegate

  17. @synthesize window;
  18. @synthesize viewController;


  19. #pragma mark -
  20. #pragma mark Application lifecycle

  21. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  22. // Override point for customization after application launch.

  23. // Add the view controller's view to the window and display.
  24. [self.window addSubview:viewController.view];
  25. [self.window makeKeyAndVisible];

  26. return YES;
  27. }

  28. - (void)dealloc {
  29. [viewController release];
  30. [window release];
  31. [super dealloc];
  32. }


  33. @end



  34. // FortuneCrunchViewController.h
  35. //

  36. #import <UIKit/UIKit.h>

  37. @interface FortuneCrunchViewController : UIViewController {

  38. IBOutlet UIButton *fortuneCookieButton;
  39. IBOutlet UILabel *fortuneLabel;

  40. }

  41. @property(nonatomic, retain) IBOutlet UIButton *fortuneCookieButton;
  42. @property(nonatomic, retain) IBOutlet UILabel *fortuneLabel;

  43. -(IBAction)crunchCookie:(id)sender;

  44. @end



  45. // FortuneCrunchViewController.m
  46. //
  47. #import "FortuneCrunchViewController.h"

  48. @implementation FortuneCrunchViewController

  49. @synthesize fortuneCookieButton;
  50. @synthesize fortuneLabel;

  51. // This method changes the cookie image when the button is pressed:

  52. -(IBAction)crunchCookie:(id)sender {
  53. NSLog(@"In crunchCookie");
  54. [fortuneCookieButton setImage:[UIImage imageNamed:@"cookie-crunched.png"]
  55. forState:UIControlStateNormal];

  56. fortuneLabel.hidden = NO;

  57. }

  58. // These methods are related to memory management:

  59. - (void)viewDidUnload {
  60. [fortuneCookieButton release];
  61. fortuneCookieButton = nil;
  62. }

  63. -(void)dealloc {
  64. [fortuneCookieButton release];
  65. [fortuneLabel release];
  66. [super dealloc];
  67. }

  68. @end



Lua code:

  1. -- AppDelegate.lua
  2. waxClass{"AppDelegate", protocols = {"UIApplicationDelegate"}}

  3. require "FortuneCrunchViewController"

  4. function applicationDidFinishLaunching(self, application)
  5. local frame = UIScreen:mainScreen():bounds()
  6. self.window = UIWindow:initWithFrame(frame)
  7. self.window:setBackgroundColor(UIColor:whiteColor())
  8. local fc = FortuneCrunchViewController:init()
  9. self.window:addSubview(fc:view())
  10. self.window:makeKeyAndVisible()
  11. end

  12. -- FortuneCrunchViewController.lua
  13. waxClass{"FortuneCrunchViewController", UIViewController}

  14. function crunchCookie(self, sender)
  15. self.button = self:view():viewWithTag(1)
  16. self.button:setImage_forState(UIImage:imageNamed('cookie-crunched.png'), UIControlStateNormal)
  17. self.label = self:view():viewWithTag(2)
  18. self.label:setHidden(false)
  19. end

  20. function init(self)
  21. self.super:initWithNibName_bundle("FortuneCrunchViewController", nil)
  22. return self
  23. end

Both projects use the same .xib created in Interface Builder, per the tutorial. Bellow are 2 screen-shots of the memory usage.


Objective-C
Obj-c memory


Wax/Lua
Lua memory

As expected, Wax has a small foot-print.

Conclusion


Most of the people dislike the syntax of Obj-C and it does slow down development. Speed and maintainability are of great value for new apps and here is an awesome alternative!


Categories: Objective C iphone mobile

40 Comments

[Review] iPhone User Interface Design Projects

iPhone User Interface Design Projects published at Apress is a great book for reading about the experience of other iPhone developers that published their applications in the AppStore and were successful. Mostly focusing on the user interface and user experience aspect, the book also covers product management, marketing, testing, tools and team dynamics.




The path of perfecting my mobile development skills is a long one and, as many paths, it’s not meant to be walked alone. Recently, one of my partners in crime shared this great book, written by a great bunch of iPhone developers, designers and entrepreneurs, about their experience of building applications for the AppStore.


Each developer get it’s own chapter, covering most of the reasoning behind making an iPhone application.


As you might have guessed from the title and by what iPhones implies, the user interface and the whole experience of using their applications gets extra attention.



Apple already provides majority of the UI objects you will need for your application. Take great care in creating new design patterns for interaction; this isn’t the Wild West of UI design like the Web can be sometimes. Respect the fact that your users have become familiar with the common objects in iPhone applications, and they will expect your application to behave the same way.
Eddie Wilson, Snow Reports



Interface constrains


Aside from the over-used statement that the screen is smaller than the desktop, the developer of Gas Cubby expresses another point about user experience:



One of the most interesting things I learned by watching users interact with my applications is that iPhone users expect tactility. Pretty much every touch of the screen should have some sort of visual reaction, even if it doesn’t actually accomplish anything. This is something that I should have learned while studying Apple’s own UI interactions, but it didn’t occur to me until I watched family and friends interact with my applications for the first time.
David Barnard, Gas Cubby



The Team


When a small team decides to make an application for the iPhone, if they want the final product to succeed, sometimes compromises must occur.



If you’re ever looking for a way to send your marriage into divorce territory, try choosing a bunch of colors with your spouse. Imagine picking a color to paint your house. Now imagine picking the colors for all the houses on your street. And imagine that the choices you make will be judged by millions of people around the world. But in the end, we came up with a winning combination that we both loved (see Figure 5-32), and we didn’t even have to involve any lawyers!
Craig Kemper, Zentomino


One of the biggest benefits of working in a two-person team with one designer and one developer is how facile we can be in implementing and modifying the design. If Jim needed me to revise a graphic, he would leave me a message (on Basecamp, of course), and I would reply within a few hours with the graphic. Certain elements could not be built in Xcode (the primary iPhone development environment) the same way I would craft them in Photoshop. We wanted to use a custom header color, for example, but the gloss is applied automatically in Xcode. For this situation, Jim quickly came up with something close, and I helped refine, through iChat, using requests like, “Could it be just a tad more saturated and a little lighter?” Altogether, we were able to get the right look in about 10 minutes.
David Kaneda, Outpost



The product approach



Before making an iPhone application, it’s obviously a good idea to check out what else is already in the App Store and make sure that you’re making a new contribution. My own experience tells me that even if you see others going for the same functionality, if they don’t get the UI right, the market is still there for the taking. Apple knows this quite well too, as it was not the first ones to make a phone with a built-in browser and music player, but it was the first to make a really slick one. (sorry Treo.)
Tim Novikoff, Flash of Genius: SAT Vocab




<Your differentiator> <your solution> for <your audience>


In my case, I’ve chosen to formulate it as follows: an effective Google Reader client for the aesthetically aware news consumer.


My differentiator is “effective”, the solution is “Google Reader client,” and my audience is “the aesthetically aware news consumer.”


By stating that the application is effective, I’m addressing all my complaints about the existing applications in one word. It’s a little bit generic and very subjective, though. Being effective will mean different things to different users. But I still find “effective” to be the best descriptor, because that’s the goal I’m trying to achieve, and it allows each user to attach individual emotions or values to being effective—and hopefully, I’ve achieved that too.


The target audience is the news consumer (i.e., people who read news). Online news feeds are implied by the saying this is a Google Reader client. And by targeting the aesthetically aware news consumer, I’m also saying that it’s a tasteful and beautiful application.”
Joachim Bondo about his upcoming feed reader



The balance



Ultimately, designers have the luxury of making iPhone applications look however they choose. The challenge of this is finding a balance between using default controls, like navigation lists, and completely customized designs that can alienate the user. As illustrated in this chapter, we created many conventions within Outpost. Some were structural, like using a flip transition to switch between two exclusive modes of the application. Some were purely cosmetic, like changing the button styles. Throughout our application design process, though, our primary goal was very straightforward—create an application that felt native and custom at the same time, had the features we wanted, and stayed out of the way of the user.
David Kaneda, Outpost




All professional designers must know a bit about programming for the environments that they design for. In my opinion, this is a requirement if you want to design for the Web, desktop, or mobile devices. Knowing the capabilities and limitations of the environment I am working in enhances my design work. I can foresee, to some extent, how a programmer will work with my designs to implement them effectively. During the implementation process, I am able to talk to the programmers and understand and diagnose issues they run into. Much of what we consider “design” lies in programming the flow and interactions of a web site or application. As an interactive designer, my job truly begins when I finish a Photoshop document. This is never truer than when designing for the iPhone.
Eddie Wilson, Snow Reports



The tools





Getting the bounce to behave realistically and at a speed that kept the application from feeling unresponsive required many iterations. To help the process, we even used a motion graphics application (Motion from Apple’s Final Cut Studio suite) to model the animation timing. Apple’s visual graphics programming tool Quartz Composer, which comes with Mac OS X, is another great way to tryout animations.
Chris Parrish and Brad Ellis, Postage



Testing



Ideally, your test subject will be somewhat familiar with the scope of your application. In my case, my wife made the perfect test subject for Trip Cubby. Her job required quite a bit of driving, and she had been tracking her reimbursable mileage with a convoluted combination of sticky notes and scribbles in her calendar. Plus, she had been using an iPhone for a few months but was definitely not a power user.


Next, you need to set up a scenario and ask the test subjects to actually use your application. The first time I had my wife test Trip Cubby, I handed her the iPhone and said, “You just drove 33 miles to give a presentation at 123 Main Street. Try entering that information into Trip Cubby.”


Once your test users are in action, it’s time for you to observe and resist the urge to intervene. It’s best to have the test subjects vocalize their thoughts as they perform the actions. By watching people interact with your application and listening to their thoughts as they walk through various scenarios, you should start seeing potential improvements in the UI. If no one figures out that your cute little disk icon means “save,” maybe you should just use the word Save onscreen. If the testers keep tapping the wrong button on a cramped toolbar, maybe you should leave a couple buttons off and make the remaining buttons bigger. If users are completely stumped, and you have to intervene with several minutes of explanation, you should consider a complete overhaul of the UI!
David Barnard, Gas Cubby



I recommend this book to anyone who's fooling around with the idea of iPhone development or who already has experience in this battle field.


Categories: Communication Design Thinking Objective C User Interaction iphone

Leave a Comment

SENATUS Wins Best Online Magazine at Digital Media Awards

SENATUS

SENATUS is one of the projects we are most proud of. It has been a long road together and recently the seeds of our efforts have started sprouting.

After seeing a huge growth in the supporting community on Facebook, now more than 62.000, on the 24th of November SENATUS received “Best Online Magazine at Digital Media Awards”. Kien M Lee, Managing Director and Founder of SENATUS, has been present at the “Asian Digital Media Awards” to receive this prize.

Given the enormous 'noise' on the Internet, it was definitely a challenge getting readers first interested, then loyal. Through sheer hard work and determination, I'm glad the fruits of labour are paying off!
Kien M Lee

Django has greatly served its purpose of being a solid framework. It’s a fantastic base for building scalable web applications, sustaining both a big number of users and an ever-increasing number of features. Liviu is doing a terrific job with it, constantly improving the SENATUS membership experience.

The cutting edge server deployment engineered and maintained by Stefan does a great job at providing high availability and top-notch performance. Cherokee running on Gentoo proves to be a peaceful and trustworthy speed demon.

All in all, it’s an honor for Odeon to be part of SENATUS and the road ahead looks exciting.

Now excuse me while I watch SENATUS TV  ;-)


Categories: Continuous Integration Design Thinking Django Gentoo Linux Product Management Python cherokee sparks talents


Tagged as: senatus social

Leave a Comment

Google App Engine Update

Note to self: After updating the Google Appengine SDK for MacOS Snow Leopard, I must run GoogleAppEngineLauncher which creates some symlinks. Otherwise importing the SDK in Python doesn't work anymore.


Categories: GAE Python

Leave a Comment

jQuery Mobile Framework

Web apps or native apps built with PhoneGap and likings allow a lot of user interface freedom. Freedom always comes at a price. In these case the price is the balance between time spent and quality of the designed user interface elements.

jQuery Mobile Framework

The current state

Sencha Touch looks like a good combination between HTML5, CSS3 and JavaScript aimed at the mobile space. I’ve got only one issue with it, how many developers use Ext JS library?

jQTouch is another library good for mobile web page and it imitates pretty closely the iPhone interface. I’ve found it so neat and fun that I’ve even made a prototype for Grokking.it using jQTouch and wrote a simple extension for it to support multi-touch events. The only problem I had with jQTouch is that it requires a pretty strict mark-up, so most of the pages are pre-loaded and some hacks were needed for dynamic pages.

The new kid

The official jQuery Mobile Framework. First of all because jQuery rocks. The community is cool, the development time it saves is amazing and the results are limitless. One thing they tried before is to make a standard for interfaces around the web trough their jQuery UI. 

jQuery Mobile Framework does look promising. Here’s a preview of some user interface elements for mobiles and tablets: http://jquerymobile.com/designs/. This does look promising and I’m more than excited to experiment with jQuery Mobile Framework, maybe even in a real life application.


Categories: Android BlackBerry Javascript iphone mobile


Tagged as: javascrip jquery

Leave a Comment

Corey Haines Talks Software Craftsmanship

Robert Dempsey and Corey Haines talk about the growing software craftsmanship movement.

"We don’t do computer science, we built business software."
Corey Haines


Categories: talents tdd

Leave a Comment

Django Test Client [quick tip]

While using the Django Test Client, sometimes views need to access request.META['HTTP_HOST'].

Sometimes the views need to retrieve request.META['HTTP_HOST']. This key is not available by default in Django's Test Client. Here's an example on how to enable it:

  1. host = 'django.testserver'
  2. client_providing_host = Client(HTTP_HOST=host)
  3. response = client_providing_host.get('/test_client/my_view/')

The default Django TestCase already contains an instance of the Test Client. Here's how to overwrite it:

  1. def setUp(self):
  2. self.client = Client(HTTP_HOST = "django.testserver")


Categories: Django tdd

Leave a Comment

Geo-location Point Inside Area

I was curious to detect if a known point is inside a polygon. Turns out that the algorithm is far simpler than I thought. Now think of this point as a person inside a building. Is the guy really inside the building?

Here is the test which can be used as an example use case:

  1. import unittest

  2. from insidePolygon import Point, is_inside_polygon

  3. class insidePolyTest(unittest.TestCase):

  4. def test_defaults(self):
  5. p = Point()
  6. self.assertEquals(p.x, 0)
  7. self.assertEquals(p.y, 0)

  8. def test_defaults(self):
  9. p = Point(3, 4)
  10. self.assertEquals(p.x, 3)
  11. self.assertEquals(p.y, 4)

  12. def test_is_inside_poly(self):
  13. poly = [Point(1,5), Point(5,5), Point(3,2), Point(1,5)]
  14. outside_point = Point(1,2)
  15. self.assertFalse(is_inside_polygon(poly, outside_point))
  16. inside_point = Point(3,4)
  17. self.assertTrue(is_inside_polygon(poly, inside_point))

  18. def test_geo_data(self):
  19. london = Point(51.501249,-0.126271)
  20. berlin = Point(52.540227,13.420944)
  21. madrid = Point(40.427994,-3.70182)
  22. rome = Point(41.922444,12.48436)
  23. paris = Point(48.860589,2.350674)
  24. bucharest = Point(44.443945,26.101341)
  25. # triangle
  26. poly = [london, berlin, madrid, london]
  27. self.assertTrue(is_inside_polygon(poly, paris))
  28. self.assertFalse(is_inside_polygon(poly, bucharest))
  29. # rectangle
  30. poly = [london, berlin, rome, madrid, london]
  31. self.assertTrue(is_inside_polygon(poly, paris))
  32. self.assertFalse(is_inside_polygon(poly, bucharest))

  33. unittest.main()

Ok. So Paris is inside the triangle made by London, Berlin and Madrid. Bucharest isn't.

Europe

Might not be a deal-breaker for some, but I'm really disappointed by "check-in" applications. First because just stating that I'm present at a certain point on the globe is not fun. Secondly, if the application associates that point to a venue, it should better check if the person checking-in is really at that venue, maybe with a small margin of error, like a 500m radius. Why am I allowed to check-in 15.000 km away and get points for that? Yes, I'm talking about Foursquare-ish apps.

Here's the Python code for calculating this:

  1. class Point:
  2. x = 0
  3. y = 0

  4. def __init__(self, x = 0, y = 0):
  5. self.x = x
  6. self.y = y

  7. def __str__(self):
  8. return "(%s, %s)" % (self.x, self.y)

  9. def max(a, b):
  10. if a > b:
  11. return a
  12. return b

  13. def min(a, b):
  14. if a < b:
  15. return a
  16. return b

  17. def is_inside_polygon(poly, point):
  18. n = len(poly) - 1
  19. counter = 0

  20. p1 = poly[0]
  21. for i in range(0, n + 1):
  22. p2 = poly[i % n]
  23. if point.y > min(p1.y, p2.y):
  24. if point.y <= max(p1.y, p2.y):
  25. if point.x <= max(p1.x, p2.x):
  26. if p1.y != p2.y:
  27. xinters = (point.y - p1.y) * (p2.x - p1.x)/(p2.y - p1.y) + p1.x
  28. if p1.x == p2.x or point.x < xinters:
  29. counter += 1
  30. p1 = p2
  31. if counter % 2 == 0:
  32. return False
  33. return True

If Elvis was using Foursquare or Koprol - the cool new Geolocation Social Network from Yahoo - we would have known when he left the building.


Categories: curiosity iphone mobile sparks

3 Comments

Facebook Photo Upload From Google App Engine

The Facebook Graph API can handle photo uploads. If no album ID has been specified, the photo is uploaded to a new album, which is named after your application. The following implementation is a Kay Framework deployment, but with a few changes can be done in other frameworks too.

The form

  1. <form enctype="multipart/form-data" action="/facebook/photo_upload/" method="POST">
  2. <input type="hidden" name="MAX_FILE_SIZE" value="100000" />
  3. <input type="hidden" name="access_token" value="{{ access_token }}" />
  4. Choose a file to upload: <input name="file" type="file" /><br />
  5. <input type="submit" value="Upload File" />
  6. </form>

Where:

access_token - OAuth2 token retrieved from Facebook. I've used the official Python SDK for Facebook Graph API. You can grab it from here, and the actual library is here..

action="/facebook/photo_upload/" - a url mapped inside my application to process the form post.

The view

This is where the form is processed. It retrieves the logged in Facebook user, then sends a multi-part post request to Facebook's API.

  1. def test_graph_api(request):
  2. import facebook, utils
  3. auth = facebook.get_user_from_cookie(request.cookies, FACEBOOK_APP_ID, FACEBOOK_SECRET)
  4. if not auth:
  5. return NotFound()
  6. access_token = auth['access_token']
  7. out = ''
  8. if request.method == "POST":
  9. out = utils.posturl('https://graph.facebook.com/me/photos', [('access_token', request.form['access_token'])],
  10. [('myfile', 'myimage.jpg', request.files['file'].stream.read())])
  11. return render_to_response('fb/index.html',
  12. {'access_token': access_token,
  13. 'out': out})

After posting to Facebook, it prints the response from Facebook which should contain the new photo's ID and should look like the code snippet bellow. Note: this is a string, not a dictionary, so you need to parse it.

  1. {"id":414686348004}

On line 9 utils.posturl() is a helper defined in the next section.

The helpers

This is the fun part, because here I post a multi-part form to Facebook.

  1. def posturl(url, fields, files):
  2. import urlparse
  3. urlparts = urlparse.urlsplit(url)
  4. return post_multipart(urlparts[1], urlparts[2], fields,files)

  1. def post_multipart(host, selector, fields, files):
  2. """
  3. Post fields and files to an http host as multipart/form-data.
  4. fields is a sequence of (name, value) elements for regular form fields.
  5. files is a sequence of (name, filename, value) elements for data to be uploaded as files
  6. Return the server's response page.
  7. """
  8. import httplib
  9. content_type, body = encode_multipart_formdata(fields, files)
  10. h = httplib.HTTPS(host)
  11. h.putrequest('POST', selector)
  12. h.putheader('content-type', content_type)
  13. h.putheader('content-length', str(len(body)))
  14. h.endheaders()
  15. h.send(body)
  16. errcode, errmsg, headers = h.getreply()
  17. return h.file.read()

  1. def encode_multipart_formdata(fields, files):
  2. """
  3. fields is a sequence of (name, value) elements for regular form fields.
  4. files is a sequence of (name, filename, value) elements for data to be uploaded as files
  5. Return (content_type, body) ready for httplib.HTTP instance
  6. """
  7. BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_'
  8. CRLF = '\r\n'
  9. L = []
  10. for (key, value) in fields:
  11. L.append('--' + BOUNDARY)
  12. L.append('Content-Disposition: form-data; name="%s"' % key)
  13. L.append('')
  14. L.append(value)
  15. for (key, filename, value) in files:
  16. L.append('--' + BOUNDARY)
  17. L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
  18. L.append('Content-Type: %s' % get_content_type(filename))
  19. L.append('')
  20. L.append(value)
  21. L.append('--' + BOUNDARY + '--')
  22. L.append('')
  23. listy = []
  24. for element in L:
  25. try:
  26. listy.append(element.decode('string_escape'))
  27. except:
  28. listy.append(element)
  29. body = CRLF.join(listy)
  30. content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
  31. return content_type, body

  1. def get_content_type(filename):
  2. import mimetypes
  3. return mimetypes.guess_type(filename)[0] or 'application/octet-stream'

This code snipped is heavily inspired by: http://code.activestate.com/recipes/146306-http-client-to-post-using-multipartform-data/, with one major change to handle binary data. From line 46 to line 52, binary data is converted in a plain string. This method is not perfect.

Who finds a better solution gets a cupcake. Leave a comment below to claim your cupcake.

Photo source: http://www.flickr.com/photos/emdot/95460346/


Categories: GAE facebook

6 Comments

Facebook Connect in Google App Engine. Making the cloud sociable

Socialble CloudGoogle App Engine is great for fast deploying and fast scaling applications. That’s a fact. The default authentication system uses Google accounts. Another option is deploying a custom user/password combination, which is the more traditional approach.

For one of our recent projects, we wanted to make the user authentication even simpler, so we integrated Facebook Connect and the Graph API. Now users can login with only one click, if they are already logged into Facebook. 

Another advantage of using this approach is avoiding boring registration forms. One click on the blue Facebook button and you’re done. User info is retrieved from Facebook, after granting the appropriate permission.

Integration

As in the past, we’ve used Kay Framework to help with some of the lower-level tasks on Google App Engine. Kay already comes with it’s own username/password authentication system, aside from the Google Accounts one. Mimicking the existing back-end, we wrote a simple one, based only on the Facebook User ID.

Facebook uses OAuth 2.0, which is token based. The token is obtained from a short “chat” between Google Appengine and the Facebook Server.

Graph API

After authenticating, using the token we can make any requests to the Graph API. Examples: retrieving user info, photo albums, uploading photos (which I’ll cover in another blog post), commenting. Head over to the Facebook Graph API docs for a full list of actions.
Since we’re developing in Python, we used Facebook’s official Python SDK, which turned out to save a lot of headaches.

Testing

In our Agile manner, tests play an important role. Kay Framework’s test client turned out to have a problem storing cookies (which we’re looking into fixing), so testing properly by doing server requests to Facebook was difficult.
One important story to test is when a user tries to access a non-public page. In that case, the user should be prompted to login with Facebook, then return to the original page.

  1. def test_fb_login_decorator(self):
  2. _create_fb_user(FACEBOOK_UID)
  3. resp = self.client.get('/projects/new/')
  4. # fb user is not logged in and attempts to access this url. redirection expected, hence 302.
  5. self.assertEquals(resp.status_code, 302)
  6. # fb user is logged in and now should be able to access the url.
  7. _facebook_login(self)
  8. resp = self.client.get('/projects/new/')
  9. self.assertEquals(resp.status_code, 200)

Source code

Here’s the link for the source code for the Kay Framework authentication back-end and some usage examples:

http://github.com/tudormunteanu/fb-auth-backend


Category: Python

2 Comments
Page generated in: 0.70s