570 likes | 648 Views
Lessons from developing an Iphone App + Server backend. Sujee Maniyam hello@sujee.net http:// sujee.net http://DiscountsForMe.net Feb 2010. Quiz. PRIZE! Where was this picture taken?. My Background. Developer (enterprise, web) Java / Php / Ruby / obj -C
E N D
Lessons from developing anIphone App + Server backend Sujee Maniyam hello@sujee.net http://sujee.net http://DiscountsForMe.net Feb 2010
Quiz • PRIZE! • Where was this picture taken?
My Background • Developer (enterprise, web) • Java / Php / Ruby / obj-C • First iphone app (Apr 2009)
Target Audience • Iphone app developers • Server backend developers for mobile apps • Expert level: Beginner - Intermediate
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
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…)
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…
Architecture • Server (DiscountsForMe.net) serves data • Server is Rails app • Iphone app talks to the server • <Insert usual SERVER ---- INTERNET CLOUD ---- IPHONEpicture here>
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
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
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()
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 )
Network setup – WIFI • Home networkover WIFI • Run local serveron laptop • Iphone + Simulatorcan connect just fine
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/
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
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
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
Agenda • Connectivity • Data format • Secure Data transfer • UDIDs, Keys, analytics • Controlling app from server
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
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/
Agenda • Connectivity • Data format • Secure Data transfer • UDIDs, Keys, multiple versions, analytics • Controlling app from server
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?
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)
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)
Metrics • Client Side metrics • Server side metrics
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)
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
Sample Server Side log data • Device_id : iphone, android, web, • Location • Ip_address • Response_time • Response_data_size • Client_key • Created_at • Updated_at
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]
Response Time … def log_access start_time = Time.now yield end_time = Time.now elapsed = ((end_time - start_time)*1000.0).to_int End
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
Response Time Chart Time (ms)
Response Size Chart • Response size (kbytes)
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
Access Keys In controller: @@keys = [ "iphone_v0.0_foobar” , "iphone_v1.0_afajiu” , "iphone_v2.0_fi98d”, "iphone_v2.0_plus_fsafa” , "android_v1.0_fasjlkuo” ] @@keys_premium = ["iphone_v2.0_plus_fsfa"]
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…)
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”);}
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]"}
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
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
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
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