Architecting iOS Apps for Fun & Profit

3bb2abb35d54dfe7c3179cb2d7c049e7?s=47 Oscar Swanros
September 23, 2015

Architecting iOS Apps for Fun & Profit

Presented at WDT September 2015.

How to architect an iOS app and have fun in the process.

3bb2abb35d54dfe7c3179cb2d7c049e7?s=128

Oscar Swanros

September 23, 2015
Tweet

Transcript

  1. Architecting iOS Apps For Fun & Profit Oscar Swanros Oscar

    Swanros, 2015
  2. Oscar Swanros Mobile Team Lead @ Reserbus ! @Swanros "

    oscar@swanros.com Oscar Swanros, 2015
  3. Somos tu primer paso para conocer lugares increíbles Oscar Swanros,

    2015
  4. So, you need a mobile app for your business Oscar

    Swanros, 2015
  5. Maybe. Oscar Swanros, 2015

  6. Avoid website wrappers Oscar Swanros, 2015

  7. Step 1: Define your needs Oscar Swanros, 2015

  8. Your needs depend on what you want to mean to

    your users. 1. ! Be fast 2. " Be reliable 3. # Be resilient 4. ✈ Be scalable 5. % Get the job done 6. & Extra! Oscar Swanros, 2015
  9. These apply to almost every project ¯\()/¯ Oscar Swanros, 2015

  10. Step 2: Design Oscar Swanros, 2015

  11. Step 3: Tooling Oscar Swanros, 2015

  12. Choose wisely. Oscar Swanros, 2015

  13. Oscar Swanros, 2015

  14. What I went with: • Swift 2.0 (!, ") •

    Frameworks! (✈) • Operation queue engine ($) • Leverage POP (✈) • Unit, UI tests (") • Beautiful UI (%) Oscar Swanros, 2015
  15. What I went with: • Swift 2.0 (!, ") •

    Frameworks! (✈) • Operation queue engine ($) • Leverage POP (✈) • Unit, UI tests (") • Beautiful UI (%) Oscar Swanros, 2015
  16. Frameworks 4 components: • RBFoundation • RBUIKit • RBNetworkKit •

    RBKit Oscar Swanros, 2015
  17. RBFoundation All the lower level stuff: • Extensions to Foundation

    & Swift types (Dictionary, NSLock, NSError, etc.) • Utils (curry, custom operators, data persistance) • Operation queue engine base implementation Oscar Swanros, 2015
  18. RBUIKit Anything that goes on or affects UI: • Dialogs

    • Network activity indicators • Custom colors • Custom fonts • Custom base UI elements Oscar Swanros, 2015
  19. RBNetworkKit Network stuff: • API Models • Data download •

    Data parsing • API Endpoint Manager • SSL pinning Oscar Swanros, 2015
  20. RBKit Reusable app tidbits: • Login screens • Image pickers

    • Specific buttons Oscar Swanros, 2015
  21. The graph • RBFoundation has no dependencies • RBUIKit depends

    on RBFoundation • RBNetworkKit depends on RBUIKit • RBKit depends on RBNetworkKit Oscar Swanros, 2015
  22. Why? Oscar Swanros, 2015

  23. The whole app relies on the Operation queue engine Oscar

    Swanros, 2015
  24. Operation • Represents an atomic unit of work • Fired

    once • Encapsulates state • Can be cancelled • NSOperation in Cocoa Oscar Swanros, 2015
  25. Operation queue • Operations run on an Operation queue •

    High-level thread • Can dispatch multiple operations at once • NSOperationQueue in Cocoa Oscar Swanros, 2015
  26. The operation queue handles 3 main things: • Operations •

    Conditions • Observers Oscar Swanros, 2015
  27. Operations are atomic units of work Oscar Swanros, 2015

  28. Operations may have conditions: stuff that needs to happen before

    the operation can be executed • Make sure we can access the internet • Make sure we can access the user's location • Make sure we can load the user's camera roll Oscar Swanros, 2015
  29. Operations may have observers: side effects • Show a network

    activity indicator whenever the app is making a network request • Update your cache after a successful download Oscar Swanros, 2015
  30. Operations may be comprised of other operations. • A GetJSONModelOperation

    needs to download and parse the JSON, but it is itself an operation • A group operation handles its own internal queue Oscar Swanros, 2015
  31. The Code Part Oscar Swanros, 2015

  32. The protocol public protocol JSONParselable { static func withData(data: [String:AnyObject])

    -> Self? } Oscar Swanros, 2015
  33. The model public final class Terminal: JSONParselable { dynamic public

    var id = 0 dynamic public var name = "name" // JSONParselable implementation public static func withData(data: [String:AnyObject]) -> Terminal? { let mkTerminal = curry { id, name in Terminal(id: id, name: name) } return toDict(data) >>>= { mkTerminal <*> int($0, "id") <*> string($0, "name") } } } Oscar Swanros, 2015
  34. Downloading Data public class DownloadJSONOperation: RBGroupOperation { public init(cacheFile: NSURL)

    { // ... } } Oscar Swanros, 2015
  35. Downloading Data - 1 let downloadTask = urlSession.downloadTaskWithRequest(request) { url,

    response, error in self.datarequestFinishedWithUrl(url, response: response, serror: error) } let networkTaskOperation = RBURLSessionTaskOperation(task: task) // Now our download task is an Operation Oscar Swanros, 2015
  36. Downloading Data - 2 let reachabilityCondition = ReachabilityCondition(host: endpointURL) networkTaskOperation.addCondition(reachabilityCondition)

    let networkObserver = NetworkActivityObserver() networkTaskOperation.addObserver(networkObserver) Oscar Swanros, 2015
  37. Finishing Downloading Data public class DownloadJSONOperation: RBGroupOperation { public init(cacheFile:

    NSURL) { // ... Previous 2 slides networkTaskOperation.addObserver(networkObserver) addOperation(networkTaskOperation) } } Oscar Swanros, 2015
  38. Parsing Data public class ParseJSONOperation<T: JSONParselable>: RBOperation { public init(cacheFile:

    NSURL) { self.cacheFile = c } override public func execute() { do { let json = try NSJSONSerialization.JSONObjectWithData(data) if let json = json { parse(json) } else { finish() } } catch let error as NSError { finishWithError(error) } } } Oscar Swanros, 2015
  39. Parsing Data - 1 private func parse(json: [String:AnyObject]) { self.parsedObjects

    = json.flatMap( T.withData($0) } process() finish() } Oscar Swanros, 2015
  40. Put it all together public class GetJSONModelOperation<T: JSONParselable>: RBGroupOperation {

    public init(query: [String:String], completion cf: Void -> Void) { super.init(operations: nil) let completion = NSBlockOperation(block: cf) parseOperation.addDependency(downloadOperation) completion.addDependency(parseOperation) addOperations([downloadOperation, parseOperation, completion]) } } Oscar Swanros, 2015
  41. Implement it public class GetTerminalsOperation: GetJSONModelOperation<Terminal> { override var downloadOperation:

    DownloadJSONOperation { return DownloadTerminalsOperation(query: query, cacheFile: cacheFile) } override var parseOperation: ParseJSONOperation<Terminal> { return ParseTerminalsOperation(cacheFile: cacheFile) } } Oscar Swanros, 2015
  42. Ship it class MyViewController: UIViewController { let operationQueue = RBOperationQueue()

    func search() { let op = GetTerminalsOperation(query: ["q":term]) { executeOnMainThread { [weak self] in self?.updateUI } } operationQueue.addOperation(q) } } Oscar Swanros, 2015
  43. Wrap! • Every app has its own needs • Think

    as the user • Architectural design goes hand in hand with UI design • Enjoy the process Oscar Swanros, 2015
  44. We're hiring. oscar@reserbus.com Oscar Swanros, 2015

  45. Questions? http://speakerdeck.com/oscarswanros Oscar Swanros, 2015