Skip to content

Commit

Permalink
Make a TodoListViewModel and factor out logic
Browse files Browse the repository at this point in the history
  • Loading branch information
twocentstudios committed Dec 8, 2015
1 parent e72fa96 commit 3eb1cff
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 25 deletions.
8 changes: 8 additions & 0 deletions todostream.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
345DF1C91C12E41900208D40 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 345DF1C81C12E41900208D40 /* Errors.swift */; };
3497F8A21C1486970068374F /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3497F8A11C1486970068374F /* Extensions.swift */; };
34F284501C172E6D000227DD /* TodoViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F2844F1C172E6D000227DD /* TodoViewModel.swift */; };
34F284521C172F49000227DD /* TodoListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F284511C172F49000227DD /* TodoListViewModel.swift */; };
34F284541C173700000227DD /* ListViewModelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F284531C173700000227DD /* ListViewModelable.swift */; };
7806A0400CDB3BD53749442C /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 750FA0576D3E75B83DA231E6 /* Pods.framework */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -58,6 +60,8 @@
345DF1C81C12E41900208D40 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
3497F8A11C1486970068374F /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
34F2844F1C172E6D000227DD /* TodoViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodoViewModel.swift; sourceTree = "<group>"; };
34F284511C172F49000227DD /* TodoListViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodoListViewModel.swift; sourceTree = "<group>"; };
34F284531C173700000227DD /* ListViewModelable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListViewModelable.swift; sourceTree = "<group>"; };
750FA0576D3E75B83DA231E6 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DBEE6C817EF9EFC263A519DE /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -106,6 +110,7 @@
children = (
345DF1981C12D2F500208D40 /* AppDelegate.swift */,
345DF19A1C12D2F500208D40 /* TodoListViewController.swift */,
34F284511C172F49000227DD /* TodoListViewModel.swift */,
34F2844F1C172E6D000227DD /* TodoViewModel.swift */,
345DF1C01C12D81D00208D40 /* TodoDetailViewController.swift */,
345DF1C21C12D82B00208D40 /* TodoDetailViewModel.swift */,
Expand All @@ -117,6 +122,7 @@
345DF1C41C12D8C100208D40 /* Event.swift */,
345DF1C81C12E41900208D40 /* Errors.swift */,
3497F8A11C1486970068374F /* Extensions.swift */,
34F284531C173700000227DD /* ListViewModelable.swift */,
345DF19F1C12D2F500208D40 /* Assets.xcassets */,
345DF1A11C12D2F500208D40 /* LaunchScreen.storyboard */,
345DF1A41C12D2F500208D40 /* Info.plist */,
Expand Down Expand Up @@ -303,9 +309,11 @@
files = (
345DF1C71C12DAA300208D40 /* TodoObject.swift in Sources */,
345DF19B1C12D2F500208D40 /* TodoListViewController.swift in Sources */,
34F284521C172F49000227DD /* TodoListViewModel.swift in Sources */,
345DF1C51C12D8C100208D40 /* Event.swift in Sources */,
345DF1C31C12D82B00208D40 /* TodoDetailViewModel.swift in Sources */,
345DF1BF1C12D7B200208D40 /* Todo.swift in Sources */,
34F284541C173700000227DD /* ListViewModelable.swift in Sources */,
345DF1BD1C12D7A500208D40 /* AppContext.swift in Sources */,
34F284501C172E6D000227DD /* TodoViewModel.swift in Sources */,
345DF1B91C12D75200208D40 /* ViewModelServer.swift in Sources */,
Expand Down
3 changes: 2 additions & 1 deletion todostream/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
self.modelServer = ModelServer(configuration: Realm.Configuration.defaultConfiguration, appContext: appContext)
self.viewModelServer = ViewModelServer(appContext: appContext)

let todoListViewController = TodoListViewController(appContext: appContext)
let todoListViewModel = TodoListViewModel()
let todoListViewController = TodoListViewController(viewModel: todoListViewModel, appContext: appContext)
let navigationController = UINavigationController(rootViewController: todoListViewController)

self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
Expand Down
36 changes: 36 additions & 0 deletions todostream/ListViewModelable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// Created by Christopher Trott on 12/9/15.
// Copyright © 2015 twocentstudios. All rights reserved.
//

import Foundation

protocol ListViewModelable {
typealias ViewModelType

var viewModels: [ViewModelType] { get }
}

extension ListViewModelable {
func numberOfRowsInSection(section: Int) -> Int {
if (section > 0) { return 0 }
return viewModels.count
}

func viewModelAtIndexPath(indexPath: NSIndexPath) -> ViewModelType {
precondition(indexPath.section == 0)
return viewModels[indexPath.row]
}
}

enum GroupChange {
case Reload
case NoOp
}

enum SingleChange {
case Insert(NSIndexPath)
case Delete(NSIndexPath)
case Reload(NSIndexPath)
case NoOp
}
50 changes: 26 additions & 24 deletions todostream/TodoListViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ final class TodoListViewController: UITableViewController {

let appContext: AppContext

var viewModels = [TodoViewModel]()
var viewModel: TodoListViewModel

init(appContext: AppContext) {
init(viewModel: TodoListViewModel, appContext: AppContext) {
self.viewModel = viewModel
self.appContext = appContext
super.init(style: .Plain)

Expand Down Expand Up @@ -47,9 +48,13 @@ final class TodoListViewController: UITableViewController {
return .empty
}
.observeNext { [unowned self] todoViewModels in
if (self.viewModels == todoViewModels) { return }
self.viewModels = todoViewModels
self.tableView.reloadData()
let change = self.viewModel.incorporateTodoViewModels(todoViewModels)
switch change {
case .Reload:
self.tableView.reloadData()
case .NoOp:
break
}
}

/// .ResponseTodoViewModel
Expand All @@ -65,19 +70,16 @@ final class TodoListViewController: UITableViewController {
return .empty
}
.observeNext { [unowned self] todoViewModel in
if let index = self.viewModels.indexOf(todoViewModel) {
if (todoViewModel.deleted) {
// delete
self.viewModels.removeAtIndex(index)
self.tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: 0)], withRowAnimation: .Right)
} else {
// replace
self.viewModels[index] = todoViewModel
self.tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: 0)], withRowAnimation: .Left)
}
} else {
self.viewModels.insert(todoViewModel, atIndex: 0)
self.tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0)], withRowAnimation: .Top)
let change = self.viewModel.incorporateTodoViewModel(todoViewModel)
switch change {
case let .Insert(indexPath):
self.tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Top)
case let .Delete(indexPath):
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Left)
case let .Reload(indexPath):
self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
case .NoOp:
break
}
}

Expand Down Expand Up @@ -134,7 +136,7 @@ final class TodoListViewController: UITableViewController {
// MARK: - UITableViewDataSource

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModels.count
return viewModel.numberOfRowsInSection(section)
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
Expand All @@ -146,26 +148,26 @@ final class TodoListViewController: UITableViewController {

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
let cell = cell as! TodoCell
cell.viewModel = viewModels[indexPath.row]
cell.viewModel = viewModel.viewModelAtIndexPath(indexPath)
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let todoViewModel = viewModels[indexPath.row]
let todoViewModel = viewModel.viewModelAtIndexPath(indexPath)
appContext.eventsObserver.sendNext(Event.RequestTodoDetailViewModel(todoViewModel))

tableView.deselectRowAtIndexPath(indexPath, animated: true)
}

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
let todoViewModel = self.viewModels[indexPath.row]
let todoViewModel = viewModel.viewModelAtIndexPath(indexPath)

let toggleCompleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.Normal, title: todoViewModel.completeActionTitle) { [unowned self] (action, path) -> Void in
let todoViewModel = self.viewModels[path.row]
let todoViewModel = self.viewModel.viewModelAtIndexPath(path)
self.appContext.eventsObserver.sendNext(Event.RequestToggleCompleteTodoViewModel(todoViewModel))
}

let deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.Destructive, title: "Delete") { [unowned self] (action, path) -> Void in
let todoViewModel = self.viewModels[path.row]
let todoViewModel = self.viewModel.viewModelAtIndexPath(path)
self.appContext.eventsObserver.sendNext(Event.RequestDeleteTodoViewModel(todoViewModel))
}

Expand Down
33 changes: 33 additions & 0 deletions todostream/TodoListViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Created by Christopher Trott on 12/9/15.
// Copyright © 2015 twocentstudios. All rights reserved.
//

import Foundation

struct TodoListViewModel: ListViewModelable {
var viewModels = [TodoViewModel]()

mutating func incorporateTodoViewModels(newViewModels: [TodoViewModel]) -> GroupChange {
if (viewModels == newViewModels) { return .NoOp }
viewModels = newViewModels
return .Reload
}

mutating func incorporateTodoViewModel(newViewModel: TodoViewModel) -> SingleChange {
if let index = viewModels.indexOf(newViewModel) {
if (newViewModel.deleted) {
// delete
viewModels.removeAtIndex(index)
return .Delete(NSIndexPath(forRow: index, inSection: 0))
} else {
// replace
viewModels[index] = newViewModel
return .Reload(NSIndexPath(forRow: index, inSection: 0))
}
} else {
viewModels.insert(newViewModel, atIndex: 0)
return .Insert(NSIndexPath(forRow: 0, inSection: 0))
}
}
}

0 comments on commit 3eb1cff

Please sign in to comment.