Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Medium TechTalks — iOS

Medium TechTalks — iOS

Grant Oladipo - Rendering the post view
Elizabeth Ford - API caching
Jimmy O'Neill - Singleton overuse
Vinicius Baggio Fuentes - iOS build systems

jimmyatmedium

August 27, 2014
Tweet

Other Decks in Programming

Transcript

  1. { "name": "8419", "type": 1, "text": "...song Walking in LA?",

    "markups": [ { "type": 3, "start": 300, "end": 315, "href": "https://somewhere.com", "title": "", "rel": "" } ] }
  2. { "name": "24cc", "type": 4, "text": "", "markups": [], "layout":

    1, "metadata": { "id": "1*WwSQ20QNvf-WovuMLLslEA.jpeg", "originalWidth": 453, "originalHeight": 301 } }
  3. Cover Flow View Post View Section View Section Content View

    Element Element Element Section Image View
  4. Cover Flow View Post View Section View Section Content View

    Element Element Element Section Image View
  5. Cover Flow View Post View Section View Section Content View

    Element Element Element Section Image View Element
  6. Cover Flow View Post View Section View Section Content View

    Element Element Element Section Image View Element
  7. Cover Flow View Post View Section View Section Content View

    Element Element Section Image View Element
  8. Cover Flow View Post View Section View Section Content View

    Element Element Section Image View Element Sect Section Section C Elemen
  9. Cover Flow View Post View View ntent View ent age

    View Element Section View Section Image View Section Content View Element Element
  10. Cover Flow View Post View Section View Section Image View

    Section Content View Element Element
  11. Cover Flow View Post View Section View Section Image View

    Section Content View Element Element
  12. Cover Flow View Post View Section View Section Image View

    Section Content View Element Element Sect Section
  13. Cover Flow View Post View View age View ntent View

    lement Section View Section Image View Section C Elemen
  14. Cover Flow View Post View Section View Section Image View

    Section Content View Element Element
  15. Cover Flow View Post View Section View Section Image View

    Section Content View Element Element
  16. Shapes & things Building the data layer of the Medium

    iOS app ! ! Elizabeth Ford @infraredflower
  17. • How will we deal with the possibility of API

    changes? • especially since the app will be long-lived • How do we minimize the number of requests from the iOS app to the server? • How can we avoid making separate API routes for the iOS app (and every client thereafter)? Questions…
  18. Clearly bad ideas • Make multiple requests to display one

    Post, User, or Collection • Make new iOS routes with exactly what we need • Only save full objects
  19. Clearly bad ideas • Save partial objects and if we

    need a full object, just check to see if all the fields are set in the object in the cache • Is the field supposed to be “” or was it never set?
  20. What about dealing with a changing API? • explicit definitions

    for the resources returned by API endpoints • tests based on these definitions
  21. Possible areas for improvement • one service to return any

    shape (instead of a separate data service for each type of object) • autogenerate shape definitions from protocol buffers (on both server and client)
  22. • easy to add features on the iOS side •

    easy to make changes on the server side without breaking the iOS client • minimize # of server requests Shapes!
  23. + (id)sharedResource { static id _instance; static dispatch_once_t onceToken; !

    dispatch_once(&onceToken, ^{ _instance = [[Resource alloc] init]; }); ! return _instance; }
  24. A singleton restricts the instantiation of a class to one

    object. They are often lazily instantiated. They can be accessed globally by any module of a program.
  25. A lot of this: Singletons are bad!! ! Not a

    lot of this: Singletons can be bad because ____. Here’s a real example!
  26. ! ! ! I’m going to focus on this: Singletons

    can be bad because ____. Here’s a real example!
  27. Time dependence isn’t an issue until the underlying data starts

    changing. PostViewController PostViewService PostService Net AuthCredentialDataService AuthCredentialDataService PostViewController
  28. If your app supports sign out, the underlying authentication data

    is changing. PostViewController PostViewService PostService Net AuthCredentialDataService AuthCredentialDataService PostViewController
  29. What problems does time dependence cause? - Functional impurity -

    Leaky abstraction - Background task failures - Difficult to test PostViewController PostViewService PostService Net AuthCredentialDataService AuthCredentialDataService PostViewController
  30. 1. User bookmark action is queued PostViewController PostViewService PostService Net

    AuthCredentialDataService Logged in dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId]; });
  31. 1. User bookmark action is queued 2. User signs out

    PostViewController PostViewService PostService Net AuthCredentialDataService Logged out LoginViewController dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId]; });
  32. 1. User bookmark action is queued 2. User signs out

    3. Bookmark action is dequeued PostViewController PostViewService PostService Net AuthCredentialDataService Logged out LoginViewController dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId]; });
  33. 1. User bookmark action is queued 2. User signs out

    3. Bookmark action is dequeued PostViewController PostViewService PostService Net AuthCredentialDataService Logged out LoginViewController dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId]; });
  34. 1. User bookmark action is queued 2. User signs out

    3. Bookmark action is dequeued PostViewController PostViewService PostService Net AuthCredentialDataService Logged out LoginViewController dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId]; });
  35. 1. User bookmark action is queued 2. User signs out

    3. Bookmark action is dequeued PostViewController PostViewService PostService Net AuthCredentialDataService Logged out LoginViewController dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId]; });
  36. 1. User bookmark action is queued 2. User signs out

    3. Bookmark action is dequeued 4. Bookmark request 401s PostViewController PostViewService PostService Net AuthCredentialDataService Logged out LoginViewController dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId]; });
  37. 1. User bookmark action is queued 2. User signs out

    3. Bookmark action is dequeued 4. Bookmark request 401s 5. PostViewController is cleaned up PostViewService PostService Net AuthCredentialDataService Logged out LoginViewController dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId]; });
  38. Option 1: YOLO Advantages! - Very little work - No

    testing needed - Funny later in your career Disadvantages! - Horrible UX - Async bookmark operation still fails - (void)logOut { // nuke everything exit(0); }
  39. Option 2: Embrace time dependence Advantages! - Doesn’t require refactoring

    Disadvantages! - Requires global knowledge of all the apps components - Affects other, disjunct service consumers - Difficult to test - Async bookmark operation still fails - (void)logOut { // Synchronously wipe all the user-dependent data [[Services authCredentialDataService] wipeData]; // … ! // Reload UI with new global data [_appController reload]; }
  40. Option 3: Dependency inject your user-dependent data - Eliminates time

    dependence in the service layer - Gracefully supports switching authentication contexts - Easy to test interfaces with a mock Session object - Async bookmark operation succeeds - (void)logOut { // Create an unauthenticated session Session *newSession = [Session loggedOutSession]; ! // Reload UI with logged out session [_appController loadSession:newSession]; }
  41. @interface Session : NSObject ! - (instancetype)initWithAuth:(AuthCredential *)authCredential; ! @property

    (nonatomic, strong, readonly) NSString *sessionId; @property (nonatomic, strong, readonly) AuthCredential *authCredential; ! - (BOOL)isAuthenticated; ! @end The Session object directly models user-dependent state, and is injected into objects that need it.
  42. PostViewService PostService Session Net - (void)fetchPostViewDataWithPostId:(NSString *)postId session:(Session *)session completion:(void(^)(Post

    *post, PostViewModel *postViewModel, PostUserData *postUserData, NSError *error))callback; PostViewService
  43. 1. User bookmark action is queued PostViewService PostService Net PostViewController

    Logged in Session dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId session:_session]; });
  44. 1. User bookmark action is queued 2. User signs out

    PostViewService PostService Net PostViewController Logged in Session LoginViewController Logged out Session dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId session:_session]; });
  45. 1. User bookmark action is queued 2. User signs out

    3. Bookmark action is dequeued PostViewService PostService Net PostViewController Logged in Session LoginViewController Logged out Session dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId session:_session]; });
  46. 1. User bookmark action is queued 2. User signs out

    3. Bookmark action is dequeued PostViewService PostService Net PostViewController Logged in Session LoginViewController Logged out Session dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId session:_session]; });
  47. 1. User bookmark action is queued 2. User signs out

    3. Bookmark action is dequeued PostViewService PostService Net PostViewController Logged in Session LoginViewController Logged out Session dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId session:_session]; });
  48. 1. User bookmark action is queued 2. User signs out

    3. Bookmark action is dequeued 4. Bookmark action succeeds PostViewService PostService Net PostViewController Logged in Session LoginViewController Logged out Session dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId session:_session]; });
  49. 1. User bookmark action is queued 2. User signs out

    3. Bookmark action is dequeued 4. Bookmark action succeeds 5. PostViewController and logged in Session are cleaned up PostViewService PostService Net LoginViewController Logged out Session dispatch_async(_backgroundQueue, ^{ [[Services postView] bookmark:_postId session:_session]; });
  50. Let’s do our best to recognize! the larger context earlier.

    PostViewController PostViewService PostService Net AuthCredentialDataService Data dependence Time dependence AuthCredentialDataService PostViewController
  51. Delivery on the Web • Feature Toggle • Tracking and

    measuring • Rolling releases • Multiple deploys a day
  52. Fast release requirements • Over-the-air updates • Crash report system

    • Authorization server • Feature toggles • Versioning system • Distribution channels
  53. OTA updates, crash reports and authorization • Crashlytics • TestFlight

    / Apple’s upcoming beta program • HockeyApp
  54. Lessons Learned • Xcconfig files and -derivedDataPath compiler flag helps

    builds being deterministic • Plist manipulation in Python, nomad-cli tools (esp. Shenzhen, Cupertino)
  55. Lessons Learned [2] • Make it easy for everyone to

    see which version of the app each group has • Easy visibility and communication on features of each of these groups • Automate everything, consider Jenkins CI over Xcode Bots
  56. Thanks! • References: • www.nomad-cli.com • jenkins-ci.org • crashlytics.com •

    https://github.com/mattt/GroundControl • http://semver.org • hockeyapp.net • http://techcrunch.com/2014/06/02/ios-testflight/ Vinícius Baggio Fuentes [email protected] @vinibaggio