Data Consistency in an Unpredictable World - Swift Summit

Db257e8558247c0658ace420677d5937?s=47 Wendy Lu
November 08, 2016

Data Consistency in an Unpredictable World - Swift Summit

At Pinterest, we recently completed a rewrite our iOS app (http://www.wired.com/2016/04/pinterest-reinvents-prove-really-worth-billions/). A lot of the work in an app rewrite goes unnoticed by the general public. Besides the sleek design and cool animations on the surface, one of the goals of our re-architecture was to move to a completely immutable model layer. This talk will focus on how we keep data consistent within our app with immutable models. We'll walk through updating models, loading and caching server data, and the architecture of our consistency framework.

Db257e8558247c0658ace420677d5937?s=128

Wendy Lu

November 08, 2016
Tweet

Transcript

  1. Consistency in an Unpredictable World

  2. Hi, I’m Wendy! @wendyluwho

  3. None
  4. None
  5. Emoji & Deep Learning !

  6. None
  7. None
  8. None
  9. 4 -> 3 dozen developers

  10. Not all users are on the best phones, or best

    network Speed is important
  11. "

  12. Network Usage (Optimizing data sent over API) App Startup Time

    Concurrency
  13. AVPlayer.resetAVAudioSessionCategoryToDefault() FBSDKSettings.configureForUseForApplication(application, withLaunchOptions:launchOptions) GSDAppIndexing.sharedInstance().registerApp(kAppStoreID) iRate.configureForUse() Adjust.appDidLaunch(adjustConfig) Stripe.configureForUse() DDLog.addLogger(DDTTYLogger.sharedInstance()) let fileLogger

    = DDFileLogger() fileLogger.logFileManager.maximumNumberOfLogFiles = 3 DDLog.addLogger(fileLogger) PIDeadlockDetector.enable() PICrash.sharedInstance().configureForUse() PINRemoteImageManager.configureForUse() CBLExperienceManager.configureForUse() NSValueTransformer.setValueTransformer(PIDateValueTransformer(), forName:kPINModelDateValueTransformerKey) CBLDeepLinkManager.sharedManager().configureServicesWithLaunchOp tions(launchOptions)
  14. Moved startup tasks to low-priority concurrent queue Reduced startup time

    by 50%
  15. AsyncDisplayKit

  16. Model Layer Immutable

  17. VC 1 VC 1 Pin Pin VC 2 ?!

  18. None
  19. Wendy Users Kanye Taylor blockUser(users[1]) #

  20. None
  21. A VC 2 Pin VC 1

  22. Updating Models

  23. Updating = Creating a new model

  24. Creating from JSON Dictionary

  25. let pin = Pin(dictionary:pinJSON) { "board" = { "created_at" =

    "Tue, 13 Aug 2013 16:38:36 +0000"; "id" = 418131215342691718; "name" = "spaces"; }; "comment_count" = 0; "description" = "At the top of my wish list for this fall is a giant chunky knit wool blanket."; "id" = "AVpd31ttshLHlWbcG9g_Kt3uVzZHjfHNvzwT20p6YnO6qzvQnqs_Z5A"; "image_square_url" = "https://s-media-cache-ak0.pinimg.com/b58cc94084407a39d62c83885ce4699e.jpg"; } Pin JSON
  26. Creating from Builder Object

  27. • imageURL = “https://www.123.com” Pin (Immutable) PinBuilder (Mutable) • title

    = “The best pin in the world” • board = “Cute Cats” • imageURL = “https://www.123.com” • title = • board = “Cute Cats” “The best pin in the world” “Meow”
  28. let pin = Pin(builder:pinBuilder)

  29. Loading and Caching

  30. “There are only two hard things in computer science: cache

    invalidation, naming things, and off-by-one errors.”
  31. image_url description user

  32. recipe

  33. PINCache

  34. Cache "id": "123" Pin123 "id" = "123" "image_url" : "http://new-url.com"

    "recipe" : {"ingredients": ["bananas"]} "imageURL" = "http://old-url.com" "board" = "Cakery" "id" = "123" "image_url" : "http://new-url.com" “board" = "Cakery" "recipe" : {"ingredients": ["bananas"]} Pin JSON
  35. Merge after initialization

  36. Observing for Changes

  37. Previously: KVO Notifications

  38. None
  39. notificationManager.addObserverForUpdatedModel(user, block:{ (NSNotification) in // Update profile view here! })

  40. NSNotificationCenter.defaultCenter().addObserverForName("name", object: nil, queue: nil) { note in // ...

    } public func addObserverForName(name: String?, object obj: AnyObject?, queue: NSOperationQueue?, usingBlock block: (NSNotification) -> Void) -> NSObjectProtocol (__NSObserver)
  41. NSNotificationCenter.defaultCenter().removeObserver(self) NSNotificationCenter.defaultCenter().removeObserver(observer)

  42. notificationManager.addObserverForUpdatedModel(user, block:{ (NSNotification) in // Update profile view here! })

  43. class NotificationManager: NSObject { private var observerTokens: [String: AnyObject] =

    [:] deinit { unregisterAll() } func unregisterAll() { for token in observerTokens.values { NSNotificationCenter.defaultCenter().removeObserver(token) } } } http://moreindirection.blogspot.com/2014/08/nsnotificationcenter-swift-and-blocks.html
  44. notificationManager.addObserverForUpdatedModel(user, block:{ (NSNotification) in // Update profile view here! })

  45. let newUser = PIUser(builder:builder) NotificationManager.postModelUpdatedNotificationWithObject(newUser)

  46. Cache postModelUpdatedNotificationWithObject(newUser) <newUser.id> “id” = “User123” “name” = “Taylor Swift”

  47. Making UI Updates

  48. notificationManager.addObserverForUpdatedModel(user, block: { [weak self] notification in if let user

    = notification.object as? PIUser { self?.user = user // Update profile view here! self?.titleLabel.text = user.name self?.imageView.setImageWithURL(user.imageURL) } })
  49. Consistency in an Unpredictable World