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

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. iOS Application
    Architecture
    Part One
    CocoaheadsSKG Dimitri James Tsiflitzis

    View Slide

  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

    View Slide

  3. Light View Controllers

    View Slide

  4. Apple MVC
    Model
    App Data
    Controller
    Updates Model
    Controls View
    View
    User
    Presentation
    Human / Non-human

    View Slide

  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

    View Slide

  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

    View Slide

  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
    }
    }

    View Slide

  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

    View Slide

  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
    }
    }

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  13. Container & Child View Controllers
    Adding a container view controller in Interface Builder
    View Controller
    Container

    View Slide

  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

    View Slide

  15. Communication

    View Slide

  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

    View Slide

  17. Interlude: Objective-C Categories, Extensions
    and Swift extensions.
    Airport
    Airport.m
    Airport+Runway.m

    View Slide

  18. Interlude: Objective-C Categories, Extensions
    and Swift extensions.
    extension SomeClass {
    /* add new functionality here */
    }

    View Slide

  19. Notification Centre
    NSNotificationCenter.defaultCenter().postNotificationName("notificationReceived", object: nil)
    This is how you post an NSNotification.

    View Slide

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

    View Slide

  21. Notification Centre
    func receiveNotification(notification : NSnotification) {
    }
    And of course, you’ll need to define the method that will be executed.

    View Slide

  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

    View Slide

  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) {
    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)
    }
    }

    View Slide

  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

    View Slide

  25. Patterns

    View Slide

  26. Patterns
    /* definition */
    class SwiftSingleton {
    static let sharedInstance = SwiftSingleton()
    private init() {}
    }
    /* usage */
    SwiftSingleton.sharedInstance
    Singletons in IOS

    View Slide

  27. Patterns
    protocol AnimationBehavior {
    func animate()
    }
    class ParallaxAnimationBehavior: AnimationBehavior {
    func animate() {
    println("Funky parallax animation code")
    }
    }
    Strategies

    View Slide

  28. Patterns
    class AnimatingView: UIView {
    var animationBehavior: AnimationBehavior?
    override func viewDidLoad() {
    super.viewDidLoad()
    animationBehavior?.animate()
    }
    }
    Strategies

    View Slide

  29. MVVM
    Decouples the View from the Model
    Moves the presentation logic out of the View Controller and into
    the View Model

    View Slide

  30. MVVM
    Model
    App Data
    Controller
    Updates Model
    Controls View
    View
    User
    Presentation
    Human / Non-human
    View
    Model

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  34. MVVM
    func displayCar() {
    self.nameLabel.text = self.viewModel.brandText;
    self.birthdateLabel.text = self.viewModel.dateText;
    }
    In our view controller

    View Slide

  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

    View Slide

  36. Ευχαριστούμε

    View Slide