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

Building Swift Libraries

Building Swift Libraries

In this talk, me and @macabeus shared some of the experiences and tips we've got while refactoring production-ready iOS/tvOS/server-side apps into small libraries in order to help our code gain readability, testability and maintainability–and our team, productivity.

Matheus Albuquerque

August 18, 2017
Tweet

More Decks by Matheus Albuquerque

Other Decks in Programming

Transcript

  1. building Swift libraries with @ythecombinator & @macabeus

  2. intro ↪modularization matters ↪package managers

  3. intro> Modularization Matters

  4. Write programs that: ↪do one thing ↪do it well ↪work

    together Ken Thompson, Doug McIlroy, Rob Pike et al.
  5. None
  6. + Libraries - Coupling

  7. + Libraries - Coupling × understanding one 㱺 understanding the

    other × testing one 㱺 testing the other × lack of reusability Coupling
  8. + Libraries - Coupling Libraries ✔ force an abstraction boundary

    on you ✔ introduce good friction × understanding one 㱺 understanding the other × testing one 㱺 testing the other × lack of reusability
  9. intermission: Namespacing, Modules and others

  10. Modules Packages Products Namespaces A namespace is something that holds

    a mapping from a particular name to a particular meaning. MyClass.hello() !" hello from app MyFramework.MyClass.hello() !" hello from framework
  11. Modules Packages Products Namespaces Each module specifies a namespace and

    enforces access controls on which parts of that code can be used outside of the module. A program may have all of its code in a single module, or it may import other modules as dependencies.
  12. Modules Packages Products Namespaces A package consists of Swift source

    files + a manifest file. A package has one or more targets. Each target specifies a product and may declare one or more dependencies.
  13. Modules Packages Products Namespaces A target may build either a

    library or an executable as its product. A library contains a module that can be imported by other Swift code. An executable is a program that can be run by the operating system.
  14. intermission: Namespacing, Modules and others ✔

  15. intro> Dependencies Managers

  16. A B C D E F

  17. $ git submodule add $ git submodule update --init --recursive

  18. $ git submodule add $ git submodule update --init --recursive

    ✔ total control over dependencies ✔ easy to include local dependencies
  19. × a lot of manual steps × error prone ×

    maintaining dependencies updates $ git submodule add $ git submodule update --init --recursive ✔ total control over dependencies ✔ easy to include local dependencies
  20. Dependency managers… ↪ simplify/standardize fetching third party code and incorporating

    it into your project ↪ pick out appropriate and compatible versions of each dependency you use
  21. None
  22. ✔ static frameworks support out of the box ✔ co"#unity

    support ✔ easy to setup and use × high abstraction over project configuration × libraries must create, update and host Podspec files × we have to manage Ruby dependencies
  23. ✔ non-invasive by default ✔ decentralized by default ✔ plain

    Swift × workarounds required when dealing with static frameworks × more manual work × slow/unstable (reports)
  24. ✔ non-invasive by default ✔ decentralized by default ✔ plain

    Swift ✔ cross-platform ✔ automated testing × Swift only × host-side only × lack of documentation
  25. hands -on ↪scaffolding ↪doing manually

  26. extra ↪testing/ coverage ↪quality ↪docs ↪CI

  27. extra> Testing

  28. focus on what the feature provides for the end user,

    not implementation
  29. https://github.com/Quick/Quick

  30. describe(".load()") { context("JSON is fetched successfully") { it("loads JSON from

    NSUserDefaults") { expect(StatsJSON.load()) .to(beTrue()) } } }
  31. context("JSON is fetched successfully") { it("loads JSON from NSUserDefaults") {

    expect(StatsJSON.load()) .to(beTrue()) } } } wrap method tests in a describe block describe(".load()") {
  32. it("loads JSON from NSUserDefaults") { expect(StatsJSON.load()) .to(beTrue()) } } }

    context("JSON is fetched successfully") { wrap scenarios into a context block describe(".load()") {
  33. wrap side-effects in multiple it blocks expect(StatsJSON.load()) .to(beTrue()) } }

    } it("loads JSON from NSUserDefaults") { describe(".load()") { context("JSON is fetched successfully") {
  34. extra> Tests Coverage

  35. https://codecov.io/

  36. extra> Code Complexity

  37. intermission: Cyclomatic Complexity

  38. E - N + 2*P E = number of edges

    in the flow graph N = number of nodes in the flow graph P = number of nodes that have exit points
  39. func containsOnlyLetters(input: String) %→ Bool { for chr in input.characters

    { if (!(chr '( "a" )* chr +, "z") )* !(chr '( "A" )* chr +, "Z") ) { return false } } return true } 1 point for the function declaration 1 point for every if, while, for and case
  40. intermission: Cyclomatic Complexity ✔

  41. intermission: NPath Complexity

  42. the number of acyclic execution paths through that function

  43. if (x) { if (y) { !" Do something }

    else { !" Do something else } } else { !" Do something even more else } As you nest control structures more deeply, the number of possible paths increases exponentially.
  44. intermission: NPath Complexity ✔

  45. https://github.com/yopeso/Taylor

  46. https://codebeat.co/

  47. extra> Documentation

  48. https://github.com/realm/jazzy

  49. https://github.com/noffle/art-of-readme

  50. https://robots.thoughtbot.com/how-to-write-a- great-readme

  51. extra> Styleguide

  52. https://github.com/realm/swiftlint

  53. extra> Continuous Integration

  54. None
  55. None
  56. https://macalogs.com.br/ desenvolvendo-pacotes-em-swift

  57. NSLog(@"Thanks!");