Some of the lessons learned from building a client-server iphone app (DiscountsForMe)
This is version 3 of the talk, presented at SF Ruby Meetup on Feb 17, 2010
4. My Background Developer (enterprise, web) Java / Php / Ruby / obj-C First iphone app (Apr 2009)
5. Target Audience Iphone app developers Server backend developers for mobile apps Expert level: Beginner - Intermediate
6. Why Client-Server Apps? Some apps run fine on the device disconnected (Tips calculator) “I think” majority of SMART apps in the future will have a server backend Some cool apps Amazon Yelp Red Laser Countless games
7. Server Backend gives you… A community (games, social interactions) Push Notification Heavy computational lifting (image recognition) Up-to date data (bar code scanners) ‘collective intelligence’ (most popular item today is…)
8. My App: DiscountsForMe Shows member benefits Based on location V2.0 in app store Memberships: Public radio (KQED, WHYY) Bank of America card AARP More…
9. Architecture Server (DiscountsForMe.net) serves data Server is Rails app Iphone app talks to the server <Insert usual SERVER ---- INTERNET CLOUD ---- IPHONEpicture here>
10. Web App / Mobile App ? What should server side code support? Are you adding mobile support for an existing web-app? Just mobile platform? (simpler ??) Hybrid (web + mobile) more work DiscountsForMe is a hybrid app
11. 1) Connectivity : Simple Start First cut : App made three server calls at startup ping() Get_X() Get_Y() Simulator Iphone over Wi-fi Iphone over 3G LAG-TIME is a problem
12. Connectivity : Minimize Lag Time Noticeable lag time over 3G/Edge Reducing lag time Show cached data Download in background Condense network calls (especially if the user is waiting for data) So, condensed call becomes Get_X() Get_Y() get_X_Y()
13. Iphone Connectivity BIG LESSON 1 : Test on IPHONE (not just simulator) Test with WiFi OFF! (3G can be slow to connect, EDGE even worse) You may need to reorganize the logic to improve response time (I had to) LESSON 2 Test in AirPlane Mode (all RADIOS off)(a frequent reason network apps are rejected )
14. Network setup – WIFI Home networkover WIFI Run local serveron laptop Iphone + Simulatorcan connect just fine
16. Network Setup for 3G Need a public IP Use a hosted server Or use your cable modem public-IP and have your router do port-forwarding DYNDNS : http://www.dyndns.com/
17. 2) Talking to Server : Format Choices : XML, JSON, other (csv, binary – protobuf/thift) JSON smaller size than XML (50% less) Json : use TouchJSON library http://code.google.com/p/touchcode/wiki/TouchJSON JSON String Touch JsonNSDictionary (yay!) XML : NSXML(sdk) / TouchXML / KissXMLhttp://www.71squared.co.uk/2009/05/processing-xml-on-the-iphone/ Rails makes it real easy to send Json/xml Some_obj.to_json Some_obj.to_xml
18. Keeping it small Trim objects No need to send all attributes Active records have extra attributes (created_at, updated_at ..etc) Example: # specify attributes to serialize obj.to_json(:only => [:name, :age]) # combine other my_response = {} my_response[:book_name] = book.name my_response[:author_name] = book.author.name render(:json => my_response.to_json()) - Compress (zip) response
19. GET vs POST iPhone SDK has a simple switch to control GET / POST What is the difference in Rails? Post requests have ‘authenticity token’ for cookie based sessions Use DB based sessions or turn off authenticity-protection
20. Agenda Connectivity Data format Secure Data transfer UDIDs, Keys, analytics Controlling app from server
21. Secure Data Transfer Plain HTTP is fine most of the time If you want to secure data Symmetric key encryption (shared ‘seckr3t’ key on Iphone app and server) Public-private key encryption (e.g. SSH) : private key on server, public key on iphone Enter : HTTPS
22. Secure data transfer : httpS SSL is ‘good enough’ for most of us Get a proper SSL certificate ($30). Self-signed certs don’t work by default Beware connection time is a little longer for httpS Verify your ssl certificate is installed properlyhttp://www.digicert.com/help/
26. Agenda Connectivity Data format Secure Data transfer UDIDs, Keys, multiple versions, analytics Controlling app from server
27. What do I send to the server? Think about including UDID (device id) And a Key (compiled within the app) http://example.com/iphone/foo?udid=xxxx&key=yyyy Why?
28. Unique Device ID (UDID) Each iphone has a unique ID, etched in hardware (just like MAC address) Your app can send UDID with each request Uses metrics on app usage Easy account creation (no signup)
29. Identify a User (Device) UDID can help you ‘auto –create’ accounts on server Eg. High scores of games Allow users to create a custom user name later Beware of a user using multiple devices (multiple UDIDs)
31. Client Side Metrics Code embedded in your iphone app Usage, Users (new, repeat), session length Few companies (Flurry, Pinch Media ..etc) Pretty easy to integrate Nice dashboards Free! (mostly)
33. Server Side Metrics why? Some things are easily measured on server side ‘collective intelligence’ Popular discounts Security audits Isolating an IP-address doing too many requests / scraping Easy to extract data / graphs ..etc Needs a bit of work on your side
34. Sample Server Side log data Device_id : iphone, android, web, Location Ip_address Response_time Response_data_size Client_key Created_at Updated_at
35. Server Side Metric : Time To Serve Want to measure the time spent on each request use around_filter in Controllerclass MyControlleraround_filter :log_access, :only => [:get_A]
36. Response Time … def log_access start_time = Time.now yield end_time = Time.now elapsed = ((end_time - start_time)*1000.0).to_int End
37. Server side Metric 2) Response Size def log_access start_time = Time.now yield end_time = Time.now elapsed = ((end_time - start_time)*1000.0).to_int response_data_size = response.body.length End
40. Access keys Keys are random, ‘sekret’ strings compiled into the iphone app Sample key = “iphone_v1.0_xklajdfoi2” (human readable + ‘hard to guess’) Start using ‘access keys’ from day-1 Each request to server must have a valid key Uses Easy to control client access (Prevent scraping, DOS ..etc) Monitoring (what versions are being used) Support multiple versions, easy upgrade
42. Supporting multiple versions May be supporting 2-3 client versions at a time (users don’t always run the latest) Keep old ‘API’ around, build-out new API if (is_v2_or_later(key)) { do something } else {do some thing else} This can get convoluted (see next page…)
44. Supporting Multiple Clients… Have different controllers handle different client versions#define SERVER @”https://foo.com/iphone1”#define SERVER @”https://foo.com/iphone2” Make sure to avoid code duplication Plan-B : End-of-life If ( ! is_supported_version(key)){send_msg(“please upgrade”);}
45. Server side : keeping it secure Make sure ‘secret stuff’ doesn’t get logged in log-files In Rails : class Mobile::MobileController < ApplicationControllerfilter_parameter_logging [:key, :uid] end Output: Processing IphoneController#get_memberships_and_discounts (for 166.137.132.167 at 2009-07-02 16:07:41) [POST] Session ID: 126e5a73742f92f85c1158ea63fd960a Parameters: {"loc"=>"39.282440,-76.765693", "action"=>"get_memberships_and_discounts", "uid"=>”[FILTERED]", "controller"=>"mobile/iphone", "dist"=>"25", "mems"=>"", "key"=>"[FILTERED]"}
46. Example : Controllers MobileController IPhoneController < MobileController AndroidController < MobileController Most of the shared logic in ‘MobileController’ Sample iPhone controllerClass IphoneController < MobileController def client_type_id 3 end end
47. Example … Class MobileController @@valid_keys = [……] def ping to_ret = {} begin validate to_ret[:status] = “OK” rescue to_ret[:error] = $1.message end render (:json => to_ret.to_json) end end
48. Example … Def validate #verify the key if (params[:key].blank?) raise DiscountsError, "dude, where is my key?" end if (params[:uid].blank?) raise DiscountsError, "dude, who are you?" end unless (@@valid_keys .has_key?(params[:key])) raise DiscountsError, "un supported version, please upgrade" end end end
50. Control … Apps changes are not easy to ‘get out’ Approval process takes time Users may not upgrade to latest version Server changes are under your control and easy to deploy So build in control-switches in the app, that can be directed from server
51. Control… One example: should display ads? show_ads : {none | admob | tapjoy} Alert Messages: “try our new version that has cool feature XYZ”
53. Hosting Shared hosting is fine, but others might swamp your DB, CPU ..etc If you can, get a VPS (Virtual Private Server) Plans start from $20 / month (SliceHost, Hosting-Rails ..etc) You have full ROOT access to the server (install packages, run CRON jobs ..etc) EC2 is great also (for testing, scaling)
54. Server : When to get it Don’t wait till TESTING phase! Get it from DAY-1, WEEK-1 Can use DNS services like DYNDNS to test on your own workstation, during development Work on easy deploy scripts Capistrano Or rsync