CocoaheadsSKG
Dimitri James Tsiflitzis
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
Model
App Data
Controller
Updates Model
Controls View
View
User
Presentation
Human / Non-human
UITableViewController
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
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
}
}
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
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
}
}
class ItemListViewController: UITableViewController {
let dataSource = ItemDataSource()
// MARK: - LIFECYCLE
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = dataSource
}
}
Just assign your data source
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
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
Adding a container view controller in Interface Builder
View Controller
Container
/* 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
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
Airport
Airport.m
Airport+Runway.m
extension SomeClass {
/* add new functionality here */
}
NSNotificationCenter.defaultCenter().postNotificationName("notificationReceived", object: nil)
This is how you post an NSNotification.
NSNotificationCenter.defaultCenter().addObserver(self,
selector:#selector(notificationReceived(_:)), name: notificationReceived, object: nil)
This is how you observe an NSNotification
func receiveNotification(notification : NSnotification) {
}
And of course, you’ll need to define the method that will be executed.
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
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)
}
}
Receiver
Knows
Sender
Many
Recipients
Notification
Notification
Two Way
Delegate
Yes
No
No
Yes
KVO
Notification
Yes
No
/* definition */
class SwiftSingleton {
static let sharedInstance = SwiftSingleton()
private init() {}
}
/* usage */
SwiftSingleton.sharedInstance
Singletons in IOS
protocol AnimationBehavior {
func animate()
}
class ParallaxAnimationBehavior: AnimationBehavior {
func animate() {
println("Funky parallax animation code")
}
}
Strategies
class AnimatingView: UIView {
var animationBehavior: AnimationBehavior?
override func viewDidLoad() {
super.viewDidLoad()
animationBehavior?.animate()
}
}
Strategies
Model
App Data
Controller
Updates Model
Controls View
View
User
Presentation
Human / Non-human
View Model
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
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
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
func displayCar() {
self.nameLabel.text = self.viewModel.brandText;
self.birthdateLabel.text = self.viewModel.dateText;
}
In our view controller
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