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

CocoaHeads SKG #8 - Application Architecture

CocoaHeads SKG #8 - Application Architecture

CocoaHeadsSKG

October 03, 2016
Tweet

More Decks by CocoaHeadsSKG

Other Decks in Technology

Transcript

  1. 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
  2. Apple MVC Model App Data Controller Updates Model Controls View

    View User Presentation Human / Non-human
  3. 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
  4. 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
  5. 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 } }
  6. 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
  7. 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 } }
  8. 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
  9. 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
  10. 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
  11. Container & Child View Controllers Adding a container view controller

    in Interface Builder View Controller Container
  12. 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
  13. 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
  14. Notification Centre func receiveNotification(notification : NSnotification) { } And of

    course, you’ll need to define the method that will be executed.
  15. 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
  16. 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) } }
  17. 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
  18. Patterns /* definition */ class SwiftSingleton { static let sharedInstance

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

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

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

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

    User Presentation Human / Non-human View Model
  23. 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
  24. 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
  25. 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
  26. 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