$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. 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).
  2. 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.
  3. 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.
  4. 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.
  5. 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?
  6. 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.
  7. 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.
  8. 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.
  9. Introduce the I in SOLID • Now lets build the

    Networking layer and see why the I or Interfaces are needed.
  10. 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…
  11. 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.
  12. 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
  13. 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.
  14. 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.
  15. 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.
  16. • 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.
  17. 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.
  18. 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.
  19. 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)
  20. 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.
  21. 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.