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

Data Consistency in an Unpredictable World - Swift Summit

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.

Wendy Lu

November 08, 2016
Tweet

More Decks by Wendy Lu

Other Decks in Technology

Transcript

  1. Consistency in
    an Unpredictable
    World

    View full-size slide

  2. Hi,
    I’m Wendy!
    @wendyluwho

    View full-size slide

  3. Emoji & Deep Learning !

    View full-size slide

  4. 4 -> 3 dozen developers

    View full-size slide

  5. Not all users are on the
    best phones, or best
    network
    Speed is important

    View full-size slide

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

    View full-size slide

  7. 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)

    View full-size slide

  8. Moved startup tasks to
    low-priority concurrent queue
    Reduced startup time by 50%

    View full-size slide

  9. AsyncDisplayKit

    View full-size slide

  10. Model Layer
    Immutable

    View full-size slide

  11. VC 1
    VC 1
    Pin
    Pin
    VC 2
    ?!

    View full-size slide

  12. Wendy
    Users
    Kanye
    Taylor
    blockUser(users[1])
    #

    View full-size slide

  13. A VC 2
    Pin
    VC 1

    View full-size slide

  14. Updating
    Models

    View full-size slide

  15. Updating = Creating a
    new model

    View full-size slide

  16. Creating from JSON
    Dictionary

    View full-size slide

  17. 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

    View full-size slide

  18. Creating from Builder
    Object

    View full-size slide

  19. • 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”

    View full-size slide

  20. let pin = Pin(builder:pinBuilder)

    View full-size slide

  21. Loading
    and
    Caching

    View full-size slide

  22. “There are only two hard
    things in computer science:
    cache invalidation, naming
    things, and off-by-one errors.”

    View full-size slide

  23. image_url
    description
    user

    View full-size slide

  24. 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

    View full-size slide

  25. Merge after initialization

    View full-size slide

  26. Observing
    for Changes

    View full-size slide

  27. Previously: KVO
    Notifications

    View full-size slide

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

    View full-size slide

  29. 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)

    View full-size slide

  30. NSNotificationCenter.defaultCenter().removeObserver(self)
    NSNotificationCenter.defaultCenter().removeObserver(observer)

    View full-size slide

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

    View full-size slide

  32. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  35. Cache
    postModelUpdatedNotificationWithObject(newUser)

    “id” = “User123”
    “name” = “Taylor Swift”

    View full-size slide

  36. Making UI Updates

    View full-size slide

  37. 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)
    }
    })

    View full-size slide

  38. Consistency in
    an Unpredictable
    World

    View full-size slide