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

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.

9949ea4cd54bd477e21ab3273a7b7186?s=128

Mark Wilkinson

April 19, 2017
Tweet

Transcript

  1. Bringing the SOLID Back. (to mobile) Mark Wilkinson - Houston

    iPhone Developer Meetup 4-18-17
  2. We See Bad, Evil Code Sometimes, we write it.

  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).
  4. Patterns and Practices

  5. That Single Responsibility thing…

  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.
  7. None
  8. Lets start the code…

  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.
  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.
  11. Lets fix it.

  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?
  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.
  14. Lets inject it instead.

  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.
  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.
  17. Introduce the I in SOLID • Now lets build the

    Networking layer and see why the I or Interfaces are needed.
  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…
  19. Fake it.

  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.
  21. Make it.

  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
  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.
  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.
  25. Lets use a protocol instead

  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.
  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.
  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.
  29. Build those LEGO pieces.

  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.
  31. Best way to imagine

  32. The O in SOLID

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