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

Building Swift Libraries

97d21da8e0ffa8f81218a293482c253a?s=47 Matheus
August 18, 2017

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.

97d21da8e0ffa8f81218a293482c253a?s=128

Matheus

August 18, 2017
Tweet

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!");