CocoaHeads SKG #8 - Application Architecture

CocoaHeads SKG #8 - Application Architecture

5a3cfb263fe4968b76c4c0bde721f047?s=128

CocoaHeadsSKG

October 03, 2016
Tweet

Transcript

  1. iOS Application Architecture Part One CocoaheadsSKG Dimitri James Tsiflitzis

  2. Intro Application architecture guides application design. Application architecture paradigms provide

    principles that influence design decisions and patterns that provide design solutions. It aims to make apps scalable, reliable, testable and manageable
  3. Light View Controllers

  4. Apple MVC Model App Data Controller Updates Model Controls View

    View User Presentation Human / Non-human
  5. Table Views and Collection Views UITableViewController Dedicated view controller for

    table views Eliminate a lot of the boilerplate code that arises by using a tableview in a view controller UITableView displayed full screen so you are restricted in your layout options
  6. override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("ItemCell", forIndexPath: indexPath) as! ItemCell let item = items[indexPath.row] as! Item cell.nameLabel.text = myItem.name return cell } You may have come across this often time Handle Cell Configuration internally
  7. Handle Cell Configuration internally Bring all of these assignments you

    see above into the table view cell subclass class ItemCell: UITableViewCell { func configure(item: Item) { self.nameLabel.text = item.name } }
  8. Handle Cell Configuration internally override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath:

    NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("ItemCell", forIndexPath: indexPath) as! ItemCell cell.configure(items[indexPath.row] as! Item) return cell } And then we have simplified the data source right away
  9. Use a Data Source Class class ItemDataSource: NSObject, UITableViewDataSource {

    let items = [Item(name:"1"), Item(name:"2"), Item(name:"3"), Item(name:"4")] // MARK: - DATA SOURCE func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items.count } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("ItemCell", forIndexPath: indexPath) as! ItemCell cell.configure(items[indexPath.row] as! Item) return cell } }
  10. Use a Data Source Class class ItemListViewController: UITableViewController { let

    dataSource = ItemDataSource() // MARK: - LIFECYCLE override func viewDidLoad() { super.viewDidLoad() tableView.dataSource = dataSource } } Just assign your data source
  11. Use a Data Source Class class ItemStore { private let

    items = [Item(name:"1"), Item(name:"2"), Item(name:"3"), Item(name:"4")] func allItems() -> [String] { return items } } Use a separate class for you items too
  12. Use a Data Source Class class ItemDataSource: NSObject, UITableViewDataSource {

    let items = ItemStore() // MARK: - DATA SOURCE func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items.allItems() } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("ItemCell", forIndexPath: indexPath) as! ItemCell cell.configure(items.allItems()[indexPath.row]) return cell } } Your final product
  13. Container & Child View Controllers Adding a container view controller

    in Interface Builder View Controller Container
  14. Container View Controllers /* add */ let viewController = self.storyBoard.instantiateViewControllerWithIdentifier("test")

    self.addChildViewController(viewController) self.view.addSubview(viewController.view) viewController.didMoveToParentViewController(self) /* remove */ let controller = self.childViewControllers.lastObject() controller.view.removeFromSuperview() controller.removeFromParentViewController() In code
  15. Communication

  16. Delegation The delegating object holds a reference to the delegate

    object and at the appropriate time sends a messages or calls a method on it Delegate Container Table View height for row
  17. Interlude: Objective-C Categories, Extensions and Swift extensions. Airport Airport.m Airport+Runway.m

  18. Interlude: Objective-C Categories, Extensions and Swift extensions. extension SomeClass {

    /* add new functionality here */ }
  19. Notification Centre NSNotificationCenter.defaultCenter().postNotificationName("notificationReceived", object: nil) This is how you post

    an NSNotification.
  20. Notification Centre NSNotificationCenter.defaultCenter().addObserver(self, selector:#selector(notificationReceived(_:)), name: notificationReceived, object: nil) This is

    how you observe an NSNotification
  21. Notification Centre func receiveNotification(notification : NSnotification) { } And of

    course, you’ll need to define the method that will be executed.
  22. Key Value Observing (KVO) Key value observing is a mechanism

    that allows objects, as specific instances, to be notified of changes on properties of other object Second Object Container First Object notifies Property A
  23. Key Value Observing (KVO) private var globalContext = 0 class

    Participant: NSObject { dynamic var name = "Maria" } class Observer: NSObject { var participant = Participant() override init() { super.init() participant.addObserver(self, forKeyPath: "name", options: .New, context: &globalContext) } override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) { if context == &globalContext { if let newValue = change?[NSKeyValueChangeNewKey] { print("Date changed: \(newValue)") } } else { super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context) } } deinit { participant.removeObserver(self, forKeyPath: "name", context: &globalContext) } }
  24. When to use the Notification Centre, Delegation or Key Value

    Observing Receiver Knows Sender Many Recipients Notification Notification Two Way Delegate Yes No No Yes KVO Notification Yes No
  25. Patterns

  26. Patterns /* definition */ class SwiftSingleton { static let sharedInstance

    = SwiftSingleton() private init() {} } /* usage */ SwiftSingleton.sharedInstance Singletons in IOS
  27. Patterns protocol AnimationBehavior { func animate() } class ParallaxAnimationBehavior: AnimationBehavior

    { func animate() { println("Funky parallax animation code") } } Strategies
  28. Patterns class AnimatingView: UIView { var animationBehavior: AnimationBehavior? override func

    viewDidLoad() { super.viewDidLoad() animationBehavior?.animate() } } Strategies
  29. MVVM Decouples the View from the Model Moves the presentation

    logic out of the View Controller and into the View Model
  30. MVVM Model App Data Controller Updates Model Controls View View

    User Presentation Human / Non-human View Model
  31. MVVM import Foundation class Car { let make : String

    let model : String let date : NSDate init(make : String, model : String, date : NSDate) { self.make = make self.model = model self.date = date } } This is a simple model that might pop up in your app and it represents a Car Model
  32. MVVM func displayCar() { let dateFormatter = NSDateFormatter() dateFormatter.dateFormat =

    "dd MMM yyyy" self.dateLabel.text = dateFormatter.stringFromDate(self.car.date) self.brandLabel.text = "\(self.car.make) \(self.car.model)" } Displaying a car in MVC
  33. MVVM class CarViewModel { let car : Car let brandText

    : String let dateText : String init(car : Car) { self.car = car self.brandText = "\(self.car.make) \(self.car.model)" let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = "dd MMM yyyy" self.dateText = dateFormatter.stringFromDate(self.car.date) } } If we have a Car view model
  34. MVVM func displayCar() { self.nameLabel.text = self.viewModel.brandText; self.birthdateLabel.text = self.viewModel.dateText;

    } In our view controller
  35. MVVM func testInitialization() { let car = Car() let carViewModel

    = CarViewModel(car: car) XCTAssertNotNil(carViewModel, "CarViewModel cannot be nil.") XCTAssertTrue(carViewModel.car === car, "Car should be equal to the car parameter") } Testing is convenient too
  36. Ευχαριστούμε