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. Write programs that: ↪do one thing ↪do it well ↪work

    together Ken Thompson, Doug McIlroy, Rob Pike et al.
  2. + Libraries - Coupling × understanding one 㱺 understanding the

    other × testing one 㱺 testing the other × lack of reusability Coupling
  3. + 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
  4. 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
  5. 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.
  6. 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.
  7. 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.
  8. $ git submodule add $ git submodule update --init --recursive

    ✔ total control over dependencies ✔ easy to include local dependencies
  9. × 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
  10. 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
  11. ✔ 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
  12. ✔ non-invasive by default ✔ decentralized by default ✔ plain

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

    Swift ✔ cross-platform ✔ automated testing × Swift only × host-side only × lack of documentation
  14. describe(".load()") { context("JSON is fetched successfully") { it("loads JSON from

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

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

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

    } it("loads JSON from NSUserDefaults") { describe(".load()") { context("JSON is fetched successfully") {
  18. 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
  19. 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
  20. 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.