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

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
  2. Outline → Fabric → GraphQL → Dependency Injection → Error

    Reporting → Fastlane 2 — "Building the Fabric iOS App" - @Javi. NSSpain 2016
  3. GraphQL - Queries → GET /users/<user_id> → GET /users/<user_id>/friends 8

    — "Building the Fabric iOS App" - @Javi. NSSpain 2016
  4. 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
  5. 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
  6. GraphQL - Features → Hierarchical, human-readable query language → Strongly-typed

    → Version free 12 — "Building the Fabric iOS App" - @Javi. NSSpain 2016
  7. 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
  8. Dependencies → View Controller => Data Provider → Data Provider

    => API Client → Persistence Manager => Database Client 16 — "Building the Fabric iOS App" - @Javi. NSSpain 2016
  9. Dependency Resolution → Self-instantiation → Global access → Injection via

    setters → Injection via constructor 17 — "Building the Fabric iOS App" - @Javi. NSSpain 2016
  10. Dependency Resolution - Self-Instantiation class ViewController { private let dataProvider:

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

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

    dataProvider: DataProvider! init() { } } 20 — "Building the Fabric iOS App" - @Javi. NSSpain 2016
  13. 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
  14. Easy - Hard Simple - Complex 23 — "Building the

    Fabric iOS App" - @Javi. NSSpain 2016
  15. Dependency injection - UIStoryboard class UIStoryboard { func instantiateViewController(withIdentifier identifier:

    String) -> UIViewController } 25 — "Building the Fabric iOS App" - @Javi. NSSpain 2016
  16. 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
  17. 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
  18. 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
  19. DataLoadState enum DataLoadState<T> { case loading case failed case loaded(T)

    } final class ApplicationListViewModel { var applications: DataLoadState<[Application]> = .loading } 31 — "Building the Fabric iOS App" - @Javi. NSSpain 2016
  20. Error Reporting class Crashlytics { public func record(_ error: NSError,

    withAdditionalUserInfo: [AnyHashable : Any]?) } 33 — "Building the Fabric iOS App" - @Javi. NSSpain 2016
  21. 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
  22. 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
  23. 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
  24. Error Reporting var allErrors: [ReportableError] { return [ [self], self.underlyingErrors.flatMap

    { $0.allErrors } // Recur ] .flatMap { $0 } } 38 — "Building the Fabric iOS App" - @Javi. NSSpain 2016
  25. 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
  26. 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
  27. 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
  28. 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