“Programs must be written for people to read, and only incidental y for machines to execute.”
- Harold Abelson, Structure and Interpretation of Computer Programs
Why Intent? • Code is a sequence of instructions • But it can’t tell us what it’s doing, or why • How can we convey our intent?
What Are We Doing? • We are problem solvers first and foremost • Can we change how we think about problem solving? • Code is only as good as our understanding • These are skil s to build our careers on
Requirements • Story: User Logs In • User enters username and password • Credentials sent to web service • If credentials are valid, store username + unique token in Keychain • Otherwise, display an error message
Responsibilities • This code has four main responsibilities: • UI handling • HTTP request sending • HTTP response parsing • Keychain storage • One problem: this is a simple requirement!
Muddled intent • Multiple responsibilities • Several levels of abstraction • Involved test setup • Two conditionals away from pain! • Employ design to increase intent
Decoupling the API • Extract the web service to a new API class • API class wil only be responsible for: • Sending requests • Parsing responses • Good class and method names further increase intent
What Did We Gain? • Named a domain concept: the web service • Encapsulated the responsibility of: • Communicating with the API • Interpreting the response • Isolated the dependency (BubbleWrap)
Testing the API • Use integration tests • Only test against API.login responsibilities • Need to mock web service response • Smal , focused classes = simpler test suites
Decoupling Keychain • Extract Keychain access out to a new class • Responsible for persisting username and token • Thus, we name it UserRegistration, rather than something involving Keychain
Testing UserRegistration • Similar to API: • Cal UserRegistration.store • Check against ACSimpleKeychain directly • Narrow interfaces ease testing
Generalizing. . • API and UserRegistration are boundaries: they al ow us to interact with the world • Boundaries typical y fal into a few categories, such as persistence, networking, and hardware sensors • However, they are dependencies. .
Dependencies • Dependencies may: • Have, or introduce bugs • Change their API across versions • Have a clunky, error-prone API • In short: they increase risk! • So, we isolate them
Why isolate? • Dependency-induced complexity obscures actual domain complexity • To prevent this, we use layers: • Domain: “what does my app do?” • Technical: “how does it accomplish it?” • Layers are required for complex systems
Dependencies And Evil “Evil things exist. You're going to have mutable state; you're going to use libraries that don't fail loudly; libraries are sometimes going to force global mutable state on you…I want that stuff on one side so that I can always keep my eye on it.. because occasional y those evil forces inhabit a body and make it wear hockey masks and swing sharp objects at me and, damn it, I'm a programmer; I can't be fighting off global- mutable-state-possessed homicidal monsters.” - Gary Bernhardt
Managing Boundaries • Name it after a domain concept • Write a simple API tailored to your app • Check for errors, and fail loudly • Rely on integration tests to verify/upgrade dependency
Pushing Onward • The control er stil knows: • That we cal the API • To store the credentials if the login succeeded • How do we test that logic? • What do we do this logic is complex?
The edge of MVC • Extract interaction to a service object, such as LoginUser that is UI agnostic • Encapsulate interaction between API and UserRegistration, simplifying our view control er • However, the API is async. How to cope?
Desired Code. .
Enter Elevate • Elevate is a global task queue designed for service objects • Robust: powered by NSOperationQueue • Start/stop operations from view control er • Simplifies async I/O in service objects
Elevate Usage • Elevate cal s execute on any object passed to the view control er’s async method • The async method returns an NSOperation to cancel the operation • Only one operation may run at a time
Elevate Simplifies IO • iOS web service requests are async • Elevate’s HTTP client: • Feels like a blocking HTTP client • Allows cancellation via NSOperation • Increased readability = increased intent
Conclusion • Maximizing intent requires practice • ‘Average’ code can be improved by simply extracting concerns out • Boundaries are necessary, but incur risk • Elevate helps you create a service layer
Further Study • Clean Architecture: http://blog.8thlight.com/uncle-bob/ 2012/08/13/the-clean-architecture.html • Destroy All Software: https://www.destroyal software.com/ • Growing Object Oriented Software Guided By Tests, by Steve Freeman & Nat Pryce