Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

Patterns and Practices

Slide 5

Slide 5 text

That Single Responsibility thing…

Slide 6

Slide 6 text

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.

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

Lets start the code…

Slide 9

Slide 9 text

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.

Slide 10

Slide 10 text

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.

Slide 11

Slide 11 text

Lets fix it.

Slide 12

Slide 12 text

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?

Slide 13

Slide 13 text

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.

Slide 14

Slide 14 text

Lets inject it instead.

Slide 15

Slide 15 text

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.

Slide 16

Slide 16 text

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.

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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…

Slide 19

Slide 19 text

Fake it.

Slide 20

Slide 20 text

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.

Slide 21

Slide 21 text

Make it.

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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.

Slide 24

Slide 24 text

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.

Slide 25

Slide 25 text

Lets use a protocol instead

Slide 26

Slide 26 text

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.

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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.

Slide 29

Slide 29 text

Build those LEGO pieces.

Slide 30

Slide 30 text

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.

Slide 31

Slide 31 text

Best way to imagine

Slide 32

Slide 32 text

The O in SOLID

Slide 33

Slide 33 text

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)

Slide 34

Slide 34 text

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.

Slide 35

Slide 35 text

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.