$30 off During Our Annual Pro Sale. View Details »

NSSpain 2016: Building Fabric.app in Swift

Javier Soto
September 16, 2016

NSSpain 2016: Building Fabric.app in Swift

Overview of some of the technical decisions made during the development of the Fabric app for iOS.

Javier Soto

September 16, 2016
Tweet

More Decks by Javier Soto

Other Decks in Programming

Transcript

  1. Building the Fabric iOS App
    @Javi
    1 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  2. Outline
    → Fabric
    → GraphQL
    → Dependency Injection
    → Error Reporting
    → Fastlane
    2 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  3. What's Fabric?
    3 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  4. Fabric
    4 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  5. Fabric
    5 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  6. GraphQL1
    1 http://graphql.org/
    6 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  7. GraphQL - Queries
    → GET /users/
    7 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  8. GraphQL - Queries
    → GET /users/
    → GET /users//friends
    8 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  9. GraphQL - Queries
    9 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  10. GraphQL - Queries
    {
    user(id: 4802170) {
    name
    profilePicture(size: 50) {
    uri
    width
    }
    friends(first: 5) {
    id
    name
    }
    }
    }
    10 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  11. GraphQL - Queries
    {
    "data": {
    "user": {
    "id": "4802170",
    "name": "Javi",
    "profilePicture": {
    "uri": "http://bit.ly/1E8lYDq",
    "width": 50
    },
    "friends": [
    {
    "id": "305249",
    "name": "Jony Ive"
    },
    {
    "id": "3108935",
    "name": "Elon Musk"
    }
    ]
    }
    }
    }
    11 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  12. GraphQL - Features
    → Hierarchical, human-readable query language
    → Strongly-typed
    → Version free
    12 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  13. GraphQL - GraphiQL
    13 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  14. Dependency Injection
    14 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  15. Dependency injection
    Software design pattern that implements inversion
    of control for resolving dependencies. An injection is
    the passing of a dependency to a dependent object
    that would use it.
    — https://en.wikipedia.org/wiki/
    Dependency_injection
    15 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  16. Dependencies
    → View Controller => Data Provider
    → Data Provider => API Client
    → Persistence Manager => Database Client
    16 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  17. Dependency Resolution
    → Self-instantiation
    → Global access
    → Injection via setters
    → Injection via constructor
    17 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  18. Dependency Resolution - Self-Instantiation
    class ViewController {
    private let dataProvider: DataProvider
    init() {
    self.dataProvider = DataProvider()
    }
    }
    18 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  19. Dependency Resolution - Global Access
    class ViewController {
    private let dataProvider: DataProvider
    init() {
    self.dataProvider = DataProvider.shared
    }
    }
    19 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  20. Dependency Resolution - Injection via Setters
    class ViewController {
    var dataProvider: DataProvider!
    init() {
    }
    }
    20 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  21. Dependency Resolution - Injection via Constructor
    class ViewController {
    private let dataProvider: DataProvider
    init(dataProvider: DataProvider) {
    self.dataProvider = dataProvider
    }
    }
    21 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  22. Easy vs Simple
    22 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  23. Easy - Hard
    Simple - Complex
    23 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  24. Dependency injection
    Implicit vs explicit
    24 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  25. Dependency injection - UIStoryboard
    class UIStoryboard {
    func instantiateViewController(withIdentifier identifier: String) -> UIViewController
    }
    25 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  26. Dependency injection - UIStoryboard
    let sb = UIStoryboard...
    let vc = sb.instantiateViewController(withIdentifier: "ID") as! ApplicationOverviewVC
    vc.userSession = ...
    vc.applicationID = ...
    26 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  27. Dependency injection - UIStoryboard
    final class ApplicationOverviewVC {
    // I hope you like "!"s...
    var userSession: UserSession!
    var applicationID: String!
    func viewDidLoad() {
    super.viewDidLoad()
    self.userSession.foo()
    // Or...
    if let userSession = self.userSession {
    userSession.foo()
    } else { /* Can't do anything useful here... */ }
    }
    }
    27 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  28. 28 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  29. Dependency injection in the Fabric app
    final class ApplicationListViewController: UIViewController {
    init(viewModel: ApplicationListViewModel)
    }
    final class ApplicationListViewModel {
    init(fabricAPI: AuthenticatedFabricAPI)
    }
    public final class AuthenticatedFabricAPI {
    public init(session: Session)
    }
    public struct Session {
    let accessToken: String
    }
    29 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  30. DataLoadState
    final class ApplicationListViewModel {
    var applications: [Application]?
    }
    30 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  31. DataLoadState
    enum DataLoadState {
    case loading
    case failed
    case loaded(T)
    }
    final class ApplicationListViewModel {
    var applications: DataLoadState<[Application]> = .loading
    }
    31 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  32. Error Reporting
    32 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  33. Error Reporting
    class Crashlytics {
    public func record(_ error: NSError, withAdditionalUserInfo: [AnyHashable : Any]?)
    }
    33 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  34. Error Reporting
    34 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  35. Error Reporting
    public protocol ReportableError: Swift.Error {
    var info: [String : Any] { get }
    var underlyingErrors: [ReportableError] { get }
    }
    35 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  36. Error Reporting
    private enum URLHandlerError: ReportableError {
    case unknownURL(URL)
    case malformedURLMissingParameter
    /// This is useful because the `NSError` conversion will only give us a "code" integer.
    private var name: String {
    switch self {
    case .unknownURL: return "UnknownURL"
    case .malformedURLMissingParameter: return "MalformedURLMissingParameter"
    }
    }
    var info: [String : Any] {
    var info: [String : Any] = ["name": self.name]
    switch self {
    case let .unknownURL(url): info["url"] = url.path ?? ""
    case .malformedURLMissingParameter: ()
    }
    return info
    }
    }
    36 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  37. Error Reporting
    extension ReportableError {
    var errorCode: Int {
    return (self as Error).code
    }
    var errorDomain: String {
    return (self as Error).domain
    }
    var asNSError: NSError {
    return NSError(
    domain: self.errorDomain,
    code: self.errorCode,
    userInfo: self.info
    )
    }
    }
    37 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  38. Error Reporting
    var allErrors: [ReportableError] {
    return [
    [self],
    self.underlyingErrors.flatMap { $0.allErrors } // Recur
    ]
    .flatMap { $0 }
    }
    38 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  39. Error Reporting
    public final class ErrorReporting {
    // We get file and function names for free
    internal func report(
    error: ReportableError,
    filePath: StaticString = #file,
    functionName: StaticString = #function,
    lineNumber: Int = #line
    ) {
    error.allErrors.forEach {
    var userInfo = $0.info
    userInfo["file"] = "\((filePath as NSString).lastPathComponent):\(lineNumber)"
    userInfo["function"] = functionName
    Crashlytics.sharedInstance().recordError($0.asNSError, withAdditionalUserInfo: userInfo)
    }
    }
    }
    39 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  40. 40 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  41. Fastlane
    → Code signing
    → Deployment
    41 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  42. Fastlane - Code Signing
    42 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  43. Fastlane - Code Signing
    https://codesigning.guide/
    43 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  44. Fastlane -
    44 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  45. Fastlane - Deployment
    45 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  46. Fastlane - Deployment
    lane :appstore do
    snapshot # Generate screenshots for the App Store
    match # Ensure code signing is set-up
    gym # Build your app
    deliver # Upload the screenshots and the binary to iTunes
    slack # Let your team-mates know the new version is live
    end
    46 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  47. Fastlane - Deployment
    lane :appstore do
    snapshot # Generate screenshots for the App Store
    match # Ensure code signing is set-up
    gym # Build your app
    deliver # Upload the screenshots and the binary to iTunes
    slack # Let your team-mates know the new version is live
    end
    $ fastlane appstore
    47 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  48. Fastlane - Deployment
    lane :appstore do |options|
    ensure_git_branch(branch: "(master|release\\S+)")
    ensure_git_status_clean
    ensure_xcode_version(version: "8.0")
    increment_version_number(version_number: options[:version_number])
    build_number = increment_build_number
    complete_version_number = "#{version_number} (#{build_number})"
    commit_version_bump(message: "Version bump to #{complete_version_number}")
    set_github_release(repository_name: "...", name: complete_version_number)
    ...
    end
    $ fastlane appstore version_number:1.6.0
    48 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  49. Fastlane.tools
    49 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  50. Questions?
    @Javi / [email protected]
    50 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide

  51. Thanks !
    51 — "Building the Fabric iOS App" - @Javi. NSSpain 2016

    View Slide