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. Emerging
    Best Practices
    in Swift
    Ash Furrow

    View Slide

  2. View Slide

  3. Afraid
    I was

    View Slide

  4. Fine
    Everything turned out

    View Slide

  5. Best Practices in Swift

    View Slide

  6. What do they look like?
    Also, how do we find new ones?

    View Slide

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

    View Slide

  8. Let’s go!

    View Slide

  9. This looks strangely familiar…

    View Slide

  10. —Lots of people, for hundreds of years
    “Those who don’t study history
    are doomed to repeat it.”

    View Slide

  11. Wow, that’s depressing.

    View Slide

  12. —Me, today
    “Those who don’t understand the past
    can’t make informed decisions about the present.”

    View Slide

  13. iOS 5 or earlier?

    View Slide

  14. 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];

    View Slide

  15. 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];

    View Slide

  16. View Slide

  17. After Object Literals
    NSArray *array = 

    @[ @"This", @"is", @"much", @"better" ];
    NSDictionary *dictionary = 

    @{ @"Who likes this?": @"Me!" };
    NSNumber *number = @(401);

    View Slide

  18. Object Literals
    • Clearly way better
    • Adopted by everyone almost immediately
    • Became a “best practice”

    View Slide

  19. ^{

    View Slide

  20. Blocks & GCD
    • Introduced in iOS 4
    • Adopted slowly, but surely
    • Required new ways of thinking
    • Did using blocks became a “best practice”?
    • Sort of…

    View Slide

  21. Enable
    Blocks
    other best practices

    View Slide

  22. ^{
    Functional Reactive Programming
    Futures
    Promises
    Collections Operations
    Callbacks
    Inline Network Operations
    Generic Datasource Objects
    Deferred Customization
    Contextual Code Execution

    View Slide

  23. Embrace
    Change

    View Slide

  24. Swift 2

    View Slide

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

    View Slide

  26. Swift 2
    • guard
    • defer
    • throws
    • etc…

    View Slide

  27. Should I use guard?

    View Slide

  28. What can I do with guard?

    View Slide

  29. Examples

    View Slide

  30. Pyramid of Doom
    if let thing = optionalThing {
    if thing.shouldDoThing {
    if let otherThing = thing.otherThing {
    doStuffWithThing(otherThing)
    }
    }
    }

    View Slide

  31. Clause Applause
    if let thing = optionalThing,
    let otherThing = thing.otherThing
    where thing.shoudDoThing {
    doStuffWithThing(otherThing)
    }

    View Slide

  32. 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
    }
    ಠ_ಠ

    View Slide

  33. Avoid Mutability
    func strings(
    parameter: [String],
    startingWith prefix: String) -> [String] {
    return parameter.filter { $0.hasPrefix(prefix) }
    }

    View Slide

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

    View Slide

  35. Before Currying
    func containsAtSign(string: String) -> Bool {
    return string.characters.contains("@")
    }
    ...
    input.filter(containsAtSign)

    View Slide

  36. Currying
    func contains(substring: String) -> (String -> Bool) {
    return { string -> Bool in
    return string.characters.contains(substring)
    }
    }
    ...
    input.filter(contains("@"))

    View Slide

  37. Currying
    func contains(substring: String)(string: String) -> Bool {
    return string.characters.contains(substring)
    }
    ...
    input.filter(contains("@"))

    View Slide

  38. Extract Associated Values
    • Use Swift enums
    • Attach associated values
    • Extract using a case

    View Slide

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

    View Slide

  40. Extract Associated Values
    enum Result {
    case Success
    case Failure(reason: String)
    }
    if case .Failure(let reason) = doThing() {
    print(" \(reason)")
    }

    View Slide

  41. Syntax
    That’s all just

    View Slide

  42. Ideas
    What matters are

    View Slide

  43. Protocol-Oriented
    Programming

    View Slide

  44. … just go watch the WWDC video.

    View Slide

  45. Others
    Let’s ask

    View Slide

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

    View Slide

  47. Try stuff
    You’ve just got to

    View Slide

  48. Ideas
    Never throw away

    View Slide

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

    View Slide

  50. Merit
    Older ideas have

    View Slide

  51. A lot
    iOS developers throw things away

    View Slide

  52. Why?

    View Slide

  53. Beginner learns thing
    Is bad at thing
    Blames thing
    Thing must be bad

    View Slide

  54. Beginner gets more experience
    New thing comes out
    Learning new thing is easier than old thing
    New thing must be good

    View Slide

  55. Appealing
    New ideas are

    View Slide

  56. Always a fresh supply of old APIs
    for us to blame
    iOS is constantly changing

    View Slide

  57. Refactoring
    Let’s talk about

    View Slide

  58. View Slide

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

    View Slide

  60. Changing a unit test?
    Refactoring Rewriting
    No Yes

    View Slide

  61. Bad
    Rewrites are

    View Slide

  62. View Slide

  63. Favour incremental change

    View Slide

  64. Code isn’t necessarily valuable
    But throwing it away is dangerous

    View Slide

  65. Things to never throw away:
    Code
    Ideas
    &

    View Slide

  66. Change
    Unit tests will help

    View Slide

  67. Unit Testing & Thinking
    • So, uhh, unit testing
    • Controversial in iOS
    • Not so much everywhere else
    • Why?
    • We’ll get to that

    View Slide

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

    View Slide

  69. Benefits of Testing
    • Limited object scope is good
    • High cohesion, low coupling
    • How to limit scope?
    • Controlling public interface and dependencies

    View Slide

  70. Dependency injection?

    View Slide

  71. Dependency Injection
    • €5 word for a ¢5 idea
    • Your things shouldn’t create the things they need

    View Slide

  72. Example

    View Slide

  73. Without Dependency Injection
    class ViewController: UIViewController {
    let networkController = NetworkController()
    func viewDidLoad() {
    super.viewDidLoad()
    networkController.fetchStuff {
    self.showStuff()
    }
    }
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  77. Dependency Injection
    protocol NetworkController {
    func fetchStuff(completion: () -> ())
    }
    ...
    class TestNetworkController: NetworkController {
    func fetchStuff(completion: () -> ()) {
    // TODO: stub fetched stuff
    completion()
    }
    }

    View Slide

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

    View Slide

  79. Without Dependency Injection
    func loadAppSetup() {
    let defaults =
    NSUserDefaults.standardUserDefaults()
    if defaults.boolForKey("launchBefore") == false {
    runFirstLaunch()
    }
    }

    View Slide

  80. How would you even test that?

    View Slide

  81. With Dependency Injection
    func loadAppSetup(defaults: NSUserDefaults) {
    if defaults.boolForKey("launchBefore") == false {
    runFirstLaunch()
    }
    }

    View Slide

  82. Don’t be an ideologue

    View Slide

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

    View Slide

  84. Cheat with Dependency Injection
    loadAppSetup() // In your app
    loadAppSetup(stubbedUserDefaults) // In your tests

    View Slide

  85. Cheat with Dependency Injection
    class ViewController: UIViewController {
    lazy var networkController: NetworkController =
    APINetworkController()
    func viewDidLoad() {
    super.viewDidLoad()
    networkController.fetchStuff {
    self.showStuff()
    }
    }
    }

    View Slide

  86. —Mackenzie King (Canada’s Winston Churchill)
    “TDD if necessary, but not necessarily TDD.”

    View Slide

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

    View Slide

  88. Unit Testing
    • So why don’t iOS developers do unit testing?
    • It’s unfamiliar and no one forces us to do it

    View Slide

  89. Better
    Testing code makes me a
    Developer

    View Slide

  90. Everything
    Abstract

    View Slide

  91. Two or more lines of repeated code?
    Find a better way

    View Slide

  92. (╯°□°)╯︵ ┻━┻

    View Slide

  93. 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!

    View Slide

  94. No such thing
    Failed experiment
    as a

    View Slide

  95. View Slide

  96. Learn
    Always opportunities to

    View Slide

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

    View Slide

  98. Make
    Tomorrow
    Better Mistakes

    View Slide