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

Emerging Best Practices in Swift

Emerging Best Practices in Swift

Presented at GOTO Copenhagen 2015: http://gotocon.com/cph-2015/

Ash Furrow

October 05, 2015
Tweet

More Decks by Ash Furrow

Other Decks in Programming

Transcript

  1. Agenda • We’ve been here before • Learning is forever,

    deal with it • Never throw ideas away • How to force yourself to think • Always be abstracting
  2. —Lots of people, for hundreds of years “Those who don’t

    study history are doomed to repeat it.”
  3. Before Object Literals NSArray *array = [NSArray arrayWithObjects: @"This", @"is",

    @"so", @"tedious", nil]; NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys: @"Who would do this?", @"Not me", nil]; NSNumber *number = [NSNumber numberWithInt:401];
  4. Before Object Literals and ARC NSArray *array = [[NSArray arrayWithObjects:

    @"This", @"is", @"so", @"tedious", nil] retain]; NSDictionary *dictionary = [[NSDictionary dictionaryWithObjectsAndKeys: @"Who would do this?", @"Not me", nil] retain]; NSNumber *number = [[NSNumber numberWithInt:401] retain];
  5. After Object Literals NSArray *array = 
 @[ @"This", @"is",

    @"much", @"better" ]; NSDictionary *dictionary = 
 @{ @"Who likes this?": @"Me!" }; NSNumber *number = @(401);
  6. Object Literals • Clearly way better • Adopted by everyone

    almost immediately • Became a “best practice”
  7. ^{

  8. Blocks & GCD • Introduced in iOS 4 • Adopted

    slowly, but surely • Required new ways of thinking • Did using blocks became a “best practice”? • Sort of…
  9. ^{ Functional Reactive Programming Futures Promises Collections Operations Callbacks Inline

    Network Operations Generic Datasource Objects Deferred Customization Contextual Code Execution
  10. Swift 2 • Lots of new syntax • New syntax

    lets us do new things • However! Syntax is only a tool • Like blocks, Swift 2 syntax is most useful when it enables new ideas
  11. Pyramid of Doom if let thing = optionalThing { if

    thing.shouldDoThing { if let otherThing = thing.otherThing { doStuffWithThing(otherThing) } } }
  12. Clause Applause if let thing = optionalThing, let otherThing =

    thing.otherThing where thing.shoudDoThing { doStuffWithThing(otherThing) }
  13. Avoid Mutability func strings( parameter: [String], startingWith prefix: String) ->

    [String] { var mutableArray = [String]() for string in parameter { if string.hasPrefix(prefix) { mutableArray.append(string) } } return mutableArray } ಠ_ಠ
  14. Avoid Mutability func strings( parameter: [String], startingWith prefix: String) ->

    [String] { return parameter.filter { $0.hasPrefix(prefix) } }
  15. Currying • One of those weird words you avoid because

    people who say it are sometimes jerks • It’s actually a pretty straightforward concept • Currying is a function that returns another function • Useful for sharing code that’s mostly the same
  16. Currying func contains(substring: String) -> (String -> Bool) { return

    { string -> Bool in return string.characters.contains(substring) } } ... input.filter(contains("@"))
  17. Extract Associated Values enum Result { case Success case Failure(reason:

    String) } switch doThing() { case .Success: print("") case .Failure(let reason): print("Oops: \(reason)") }
  18. Extract Associated Values enum Result { case Success case Failure(reason:

    String) } if case .Failure(let reason) = doThing() { print(" \(reason)") }
  19. Syntax vs Idea • How to tell if something is

    universally a good idea, or just enables other ideas? • You can’t • It’s a false dichotomy • I lied to you • I’m so sorry
  20. Never Throw Away Ideas • Swift was released • We

    treated Swift like object literals instead of like blocks • Some of us thought Swift was universally better • My fault, oops
  21. Beginner gets more experience New thing comes out Learning new

    thing is easier than old thing New thing must be good
  22. Always a fresh supply of old APIs for us to

    blame iOS is constantly changing
  23. What is Not Refactor? • Refactoring does not add new

    functionality • Refactoring does not change a type’s interface • Refactoring does not change a type’s behaviour
  24. Unit Testing & Thinking • So, uhh, unit testing •

    Controversial in iOS • Not so much everywhere else • Why? • We’ll get to that
  25. Benefits of Testing • (Let’s presume that unit testing is

    a good idea) • I really don’t care that much about the tests • I care more about how writing tests makes me think about what I’m writing
  26. Benefits of Testing • Limited object scope is good •

    High cohesion, low coupling • How to limit scope? • Controlling public interface and dependencies
  27. Dependency Injection • €5 word for a ¢5 idea •

    Your things shouldn’t create the things they need
  28. Without Dependency Injection class ViewController: UIViewController { let networkController =

    NetworkController() func viewDidLoad() { super.viewDidLoad() networkController.fetchStuff { self.showStuff() } } }
  29. With Dependency Injection class ViewController: UIViewController { var networkController: NetworkController?

    func viewDidLoad() { super.viewDidLoad() networkController?.fetchStuff { self.showStuff() } } }
  30. Dependency Injection • Rely on someone else to configure your

    instance • Could be another part of your app (eg: prepareForSegue) • Could be a unit test • Protocols work really well for this
  31. Dependency Injection protocol NetworkController { func fetchStuff(completion: () -> ())

    } ... class APINetworkController: NetworkController { func fetchStuff(completion: () -> ()) { // TODO: fetch stuff and call completion() } }
  32. Dependency Injection protocol NetworkController { func fetchStuff(completion: () -> ())

    } ... class TestNetworkController: NetworkController { func fetchStuff(completion: () -> ()) { // TODO: stub fetched stuff completion() } }
  33. Dependency Injection • Use of protocols limits coupling between types

    • Adding a method to a protocol becomes a decision you have to make • Dependency injection can also be used for shared state, like singletons
  34. Without Dependency Injection func loadAppSetup() { let defaults = NSUserDefaults.standardUserDefaults()

    if defaults.boolForKey("launchBefore") == false { runFirstLaunch() } }
  35. Cheat with Dependency Injection func loadAppSetup( defaults: NSUserDefaults = .standardUserDefaults()){

    if defaults.boolForKey("launchBefore") == false { runFirstLaunch() } }
  36. Cheat with Dependency Injection class ViewController: UIViewController { lazy var

    networkController: NetworkController = APINetworkController() func viewDidLoad() { super.viewDidLoad() networkController.fetchStuff { self.showStuff() } } }
  37. Unit Testing • Don’t test private functions • Also, start

    marking functions as private • Remember, we want to avoid rewriting • Don’t test the implementation • Don’t use “partial mocks” • See @searls post on partial mocks
  38. Unit Testing • So why don’t iOS developers do unit

    testing? • It’s unfamiliar and no one forces us to do it
  39. Look for Abstractions • You’re already learning new syntax •

    Look for new abstractions along the way • Not all ideas will work out • But you should still do it • Experiment!
  40. Wrap Up • We have a history of being awesome,

    let’s keep it up • Learning isn’t just for when Xcode is in beta • Ideas are more valuable than code, but throwing away either is dangerous • Effective unit tests make it easy to change code • Operate at the highest level of abstraction you can at any given time