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

iOS Architectures - Art or Science?

iOS Architectures - Art or Science?

Brief overview over the architecture I'm using for iOS applications. MVVM with Rx and Protocol Oriented Programming. Bonus of dependencies, tools and project structure.

Ivan Bruel

May 19, 2016
Tweet

More Decks by Ivan Bruel

Other Decks in Programming

Transcript

  1. MVC • Apple's intended architecture for iOS • First architecture

    for most of us • Fast to learn and develop • MVC = Massive View Controllers? • Hard to test
  2. MVVM • Goals: • Reduce complexity of View Controllers •

    Make presenta<on logic easier to test • View Models: • Map the model data into presentable data • View Controllers: • set view dodel data into views • UI interac<ons
  3. import ObjectMapper struct Animal: Mappable { var name: String! var

    species: String! init(name: String, species: String) { self.name = name self.species = species } init?(_ map: Map) { } mutating func mapping(map: Map) { name <- map["name"] species <- map["species"] } }
  4. import RxSwift import Moya class AnimalDetailViewModel { private let animal:

    Animal private let hungry = Variable<Bool>(true) private let disposeBag = DisposeBag() let name: String var stateOfMind: Observable<String> { return hungry.asObservable() .map { $0 ? "!" : """ } } init(animal: Animal) { self.animal = animal name = "\(animal.name) the \(animal.species)" } func feed() { Network.provider.request(API.Feed(animal.name)) .subscribeNext { [weak self] response in if response.isSuccess { self?.hungry.value = false } }.addDisposableTo(disposeBag) } }
  5. import RxSwift class AnimalDetailViewController: UIViewController { @IBOutlet private weak var

    nameLabel: UILabel! @IBOutlet private weak var stateOfMindLabel: UILabel! @IBOutlet private weak var feedButton: UIButton! private let disposeBag = DisposeBag() var viewModel: AnimalViewModel! override func viewDidLoad() { super.viewDidLoad() bindViewModel() } private func bindViewModel() { nameLabel.text = viewModel.name viewModel.stateOfMind .bindTo(stateOfMindLabel.rx_text) .addDisposableTo(disposeBag) feedButton.rx_tap .bindNext { [weak self] _ in self?.feed() }.addDisposableTo(disposeBag) } }
  6. import Moya_ObjectMapper import RxSwift class AnimalListViewModel { private let animals:

    Variable<[Animal]> private let disposeBag = DisposeBag() var viewModels: Observable<[AnimalListItemViewModel]> { return animals.asObservable() .map { AnimalListItemViewModel(animal: $0) } } func detailViewModelForIndex(index: Int) { return AnimalDetailViewModel(animal: animals.value[index]) } func getAnimals() { Network.provider.request(API.Animals) .mapArray(Animal) .subscribeNext { [weak self] animals in self?.animals.value.append(animals) }.addDisposableTo(disposeBag) } }
  7. class AnimalListItemViewModel { private let animal: Animal let name: String

    let species: String init(animal: Animal) { self.animal = animal name = animal.name species = animal.species } }
  8. class AnimalTableViewCell: UITableViewCell, ReusableCell { @IBOutlet private weak var nameLabel:

    UILabel! @IBOutlet private weak var speciesLabel: UILabel! var viewModel: AnimalListItemViewModel! { didSet { nameLabel.text = viewModel.name speciesLabel.text = viewModel.species } } }
  9. protocol ReusableCell { var reuseIdentifier: String? { get } }

    extension ReusableCell where Self: UITableViewCell { var reuseIdentifier: String? { return Self.className } } extension UITableView { func dequeueReusableCell<T: UITableViewCell>(type: T.Type, indexPath: NSIndexPath) -> T { return dequeueReusableCellWithIdentifier(T.className, forIndexPath: indexPath) as? T ?? T() } }
  10. class AnimalListViewController: UIViewController { @IBOutlet private weak var tableView: TableView!

    private let disposeBag = DisposeBag() var viewModel: AnimalListViewModel! override func viewDidLoad() { super.viewDidLoad() bindViewModel() } private func bindViewModels() { viewModel.viewModels .bindTo(tableView.rx_itemsWithCellFactory) { (tableView: UITableView, indexPath: NSIndexPath, viewModel: AnimalListItemViewModel) in let cell = tableView .dequeueReusableCell(AnimalTableViewCell.self, index: indexPath) cell.viewModel = viewModel return cell }.addDisposableTo(disposeBag) } }
  11. extension AnimalListViewController { override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

    guard let animalVC = segue.destinationViewController as? AnimalDetailViewController, index = sender as? Int else { return } animalViewController = viewModel .detailViewModelForIndex(index) } }
  12. Pods I'cant live without • RxSwi&: Reac(veX for Swi1 •

    RxCocoa: RxSwi1 Cocoa extensions • RxDataSources: Table and Collec(on View Data Sources for RxSwi1 • RxOp3onal: RxSwi1 extensions for Swi1 op(onals and "Occupiable" types • Result: type modelling the success/failure of arbitrary opera(ons. • Moya: Network abstrac(on layer wriIen in Swi1. • ObjectMapper: convert your model objects (classes and structs) to and from JSON. • SnapKit: DSL to make Auto Layout easy on iOS • Kingfisher: A lightweight library for downloading and caching image from the web. • Quick: behavior-driven development framework for Swi1 • Nimble: A Matcher Framework for Swi1
  13. Tools I can't live without • Swi$Gen: Swi'Gen auto-generates Swi'

    code for various assets, strings, storyboards and colors of your project • Synx: A command-line tool that reorganizes your Xcode project folder to match your Xcode groups. • Swi$lint: A tool to enforce Swi' style and convenCons, loosely based on GitHub's Swi' Style Guide • Crashly1cs: powerful, yet lightweight reporCng soluCon for iOS • Fastlane: The easiest way to automate building and releasing your iOS Apps
  14. Project Structure Project ├── App │ └── AppDelegate ├── Enums

    ├── Extensions ├── Externals ├── Globals ├── Helpers ├── Models ├── Networking ├── Protocols ├── Resources │ ├── LaunchScreen.storyboard │ ├── Localizable.strings │ └── Info.plist ├── Structs ├── ViewControllers │ ├── Onboarding │ │ └── Onboarding.storyboard │ └── Main │ └── Main.storyboard ├── ViewModels └── Views