$30 off During Our Annual Pro Sale. View Details »

SOLID coding in Swift and iOS

SOLID coding in Swift and iOS

Bringing back the SOLID to mobile development, presented to the Houston iPhone User Group.

Mark Wilkinson

April 19, 2017
Tweet

More Decks by Mark Wilkinson

Other Decks in Technology

Transcript

  1. Bringing the SOLID Back.
    (to mobile)
    Mark Wilkinson - Houston iPhone Developer Meetup
    4-18-17

    View Slide

  2. We See Bad, Evil Code
    Sometimes, we write it.

    View Slide

  3. Mitigation
    • Name some ways to prevent said bad code (coming
    from you):
    • Quit and do something else for monies?
    • have someone else write all your code, basically be
    a manager or pm?
    • Actually learn patterns and practices, learn from
    your mistakes (most of us).

    View Slide

  4. Patterns and Practices

    View Slide

  5. That Single Responsibility
    thing…

    View Slide

  6. That Single Responsibility
    thing…
    • If all you ever get out of the S in SOLID is this, that’s
    fine.
    • So much code could be cleaned up and fixed if it
    followed this rule, of having 1 and only 1 job.
    • Think of your code when you see it… “YOU HAD ONE
    JOB CLASS/Function/Module, ONE JOB.”
    • Even Microsoft and Apple example/starter code
    violates this.

    View Slide

  7. View Slide

  8. Lets start the code…

    View Slide

  9. What we did…
    • Made the AppDelegate continue to do only 1 thing.
    • The CoreDataHelper does only 1 thing (provide an
    interface to the CoreData Stack) and can be accessed
    from anywhere with sharedInstance.
    • We also introduced the beginning of a pattern that’s
    extremely useful in any app architecture…
    • It’ll be the D in SOLID, but more on that later.

    View Slide

  10. Now we need a data layer
    • Lets say we want to have a friendly interface to
    fetching cached data (whether from user input or last
    web service call, etc).
    • Put that in the CoreDataHelper? Let’s try it out.
    • Well, now the CoreDataHelper is promising Weather
    days? Sounds like that’s not it’s job.
    • Move it out, create a new job for someone else.

    View Slide

  11. Lets fix it.

    View Slide

  12. Now the source of Data is a
    black box
    • CoreDataHelper returns to doing 1 job.
    • New WeatherDataSource promises to do 1 job, give
    back Weather data. It will now use the
    CoreDataHelper.
    • The abstraction now lets the ViewController worry
    about getting actual WeatherData and not manipulating
    the CD Stack.
    • Wait… Who is going to give the ViewController the
    WeatherDataSource?

    View Slide

  13. The D in SOLID
    • Now we’re skipping around, that’s okay
    • Dependencies keep the modules/parts safely
    independent while continuing to do their 1 intended
    job.
    • WeatherDataSource is now a dependency of
    ViewController
    • CoreDataHelper is now a dependency of
    WeatherDataSource.

    View Slide

  14. Lets inject it instead.

    View Slide

  15. What we did
    • Injected (or handed) the ViewController an instance of the
    WeatherDataSource, without having IT instantiate itself.
    • We also handed WeatherDataSource an instance of
    CoreDataHelper for the same reasons.
    • Notice there’s a chain here, of one class handing another
    the dependencies it needs.
    • This is called Dependency Injection and it helps insure
    that classes only worry about what they promise to do
    (that 1 thing) any other work is done by its dependencies.

    View Slide

  16. DI continued
    • ViewController got it through Property Injection, the
    WeatherDataSource through Initializer Injection.
    • Either is fine, use whatever suits your needs best.
    • Usually, because of storyboards, ViewControllers will
    need to get it through property injection, all other
    classes though that can be initialized, should get it
    through their inits.

    View Slide

  17. Introduce the I in SOLID
    • Now lets build the Networking layer and see why the I
    or Interfaces are needed.

    View Slide

  18. What we did.
    • Created a client class to use NSURLSession that at
    some point will return data from a service.
    • Lets imagine the endpoint isn’t ready yet (cause that’s
    never happened in a mobile project before).
    • Best case, and you should do this at the start of
    EVERY app…

    View Slide

  19. Fake it.

    View Slide

  20. Faking Data
    • In most instances, you’ll get JSON back, so write
    some JSON.
    • Best bet is to keep a file handy in the project with
    JSON that you believe will come back from your
    service.
    • return that JSON as Data() in your fake client.

    View Slide

  21. Make it.

    View Slide

  22. What we did.
    • WeatherDataSource will use a WebClient either real or
    fake to get back Data, from there it’ll parse and form
    into NSManagedObjects.
    • The WebClient will be injected into the
    WeatherDataSource as well.
    • In doing this the WeatherDataSource will be the glue
    between CoreDataHelper and the WebClient
    • From there the ViewController will get back first a
    cached set of data (from the stack), then when the
    completion block completes, it’ll get updated data

    View Slide

  23. A few more things…
    • Notice the AppDelegate is now pretty much handling
    the injections of dependencies two levels beneath it.
    • Also notice we’re hardcoding the dependency to be a
    FakeWebClient, not a common client that could be
    either real or fake.
    • I know the urge… subclass, but don’t, just don’t.
    avoid Subclassing with this pattern at ALL COSTS.

    View Slide

  24. What should we do then?
    • Notice the FakeWebClient and the WebClient adhere
    to a common interface:
    • Here’s our I in SOLID, we need an Inteface.
    • Of course in Swift and Objective-C they’re protocols.

    View Slide

  25. Lets use a protocol instead

    View Slide

  26. Done.
    • Now the webClients adhere to the Client protocol.
    • The protocol/interface can be injected instead,
    increasing flexibility
    • This makes it easier to swap out the fake from the real
    client by changing 1 line.

    View Slide

  27. • This is the selling point of Dependency Injection, it’s
    easy to swap out pieces for completely different
    behaviors (even at runtime) with minimal effort.

    View Slide

  28. But wait, we’re not done.
    • CoreData is a hefty stack.
    • What if for faster prototyping we need something
    simpler, like NSUserDefaults (UserDefaults now in
    Swift 3)
    • Hmm… sounds like a familiar situation.

    View Slide

  29. Build those LEGO pieces.

    View Slide

  30. What we did.
    • WeatherDataSource still uses a Client interface, but
    now uses a new interface to persisted data.
    • The Persistence protocol will be injected into the
    WeatherDataSource instead of the specific
    CoreDataHelper.
    • In doing this the WeatherDataSource no longer needs
    to know about the CoreData stack, just a layer
    persisting to disk.
    • Again, we’re following the rules of modules knowing
    less and less and doing only what they need to do.

    View Slide

  31. Best way to imagine

    View Slide

  32. The O in SOLID

    View Slide

  33. Exactly what we did
    • We extended functionality of the app, instead of
    changing it.
    • By using protocols, new implementations like
    UserDefaults instead of CoreData could be swapped
    out (again at runtime if need be).
    • The app didn’t change, it was just enhanced with more
    capabilities for doing the same task (persisting data, or
    fetching JSON)

    View Slide

  34. Bonus Round
    • AppDelegate shouldn’t worry about injecting, that
    should be a job for someone else.
    • In comes a Dependency Resolver.
    • A class that simply registers protocols with
    implementations (with a Dictionary).
    • Even better, create Xcode build schemes, one for
    fakes, one for reals, where certain lines of code in the
    Resolver are run for each.

    View Slide

  35. Summary
    • We created a better App stack.
    • Again, imagine Lego pieces and not lines of code.
    • Use Design Patterns only when it suits the
    situation.
    • The Dependency Resolver is a good example of
    the Factory Pattern.

    View Slide