Last active
August 29, 2015 14:14
-
-
Save justinmakaila/74494df57c237b7ce559 to your computer and use it in GitHub Desktop.
Uses ReactiveCocoa to provide a view controller, protocol, and view model to present a simple table-driven feed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// FeedViewController.swift | |
// ProjectDemand | |
// | |
// Created by Justin Makaila on 1/13/15. | |
// Copyright (c) 2015 Present. All rights reserved. | |
// | |
import UIKit | |
import TableAdapter | |
import ReactiveCocoa | |
protocol FeedViewModelProtocol { | |
var isEmpty: Bool { get } | |
func refreshSignal() -> RACSignal | |
func loadMoreSignal() -> RACSignal | |
} | |
class FeedViewModel: NSObject, FeedViewModelProtocol { | |
var isEmpty: Bool { | |
return true | |
} | |
func refreshSignal() -> RACSignal { | |
return RACSignal.empty() | |
} | |
func loadMoreSignal() -> RACSignal { | |
return RACSignal.empty() | |
} | |
} | |
class FeedViewController: UIViewController { | |
// MARK: - View Model | |
var viewModel: FeedViewModelProtocol! | |
// MARK: - General Signals | |
var networkingComplete: RACSignal! | |
var networkingError: RACSignal! | |
var executingNetworkRequest: RACSignal! | |
// MARK: - Empty Signals | |
var isEmptySignal: RACSignal! | |
// MARK: - Refresh | |
private(set) var refreshCommand: RACCommand! | |
var refreshExecuting: RACSignal! | |
var refreshSuccess: RACSignal! | |
var refreshError: RACSignal! | |
// MARK: - Load More | |
private(set) var loadMoreCommand: RACCommand! | |
var loadMoreExecuting: RACSignal! | |
var loadMoreSuccess: RACSignal! | |
var loadMoreError: RACSignal! | |
// MARK: - Subviews | |
private let refreshControl = UIRefreshControl() | |
// MARK: IBOutlets | |
@IBOutlet internal var emptyStateView: UIView? | |
@IBOutlet internal var tableView: UITableView! | |
// MARK: - Table View Adapter | |
// MARK: Table View Data Source | |
let tableDataSource = TableViewDataSource() | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
setup() | |
} | |
override func viewWillAppear(animated: Bool) { | |
super.viewWillAppear(animated) | |
if viewModel.isEmpty { | |
refresh() | |
} | |
} | |
func refresh() { | |
refreshCommand.execute(nil) | |
} | |
func loadMore() { | |
loadMoreCommand.execute(nil) | |
} | |
} | |
// MARK: - Setup Extension | |
private extension FeedViewController { | |
func setup() { | |
setupTableView() | |
setupSignals() | |
} | |
func setupTableView() { | |
// Insert the refresh control | |
tableView.insertSubview(refreshControl, atIndex: 0) | |
// Set `tableDataSource` table view to `tableView` | |
tableDataSource.tableView = tableView | |
} | |
func setupSignals() { | |
setupNetworkingSignals() | |
refreshControl.rac_command = refreshCommand | |
} | |
func setupRefreshSignals() { | |
// Create a refresh command for handling refresh | |
refreshCommand = RACCommand { _ in | |
return self.viewModel.refreshSignal() | |
} | |
// Refresh command should not allow concurrent execution | |
refreshCommand.allowsConcurrentExecution = false | |
// Configure the refresh signals | |
refreshExecuting = refreshCommand.executing | |
refreshSuccess = refreshCommand.executionSignals.flattenMap { $0 as RACSignal } | |
refreshError = refreshCommand.errors | |
} | |
func setupLoadMoreSignals() { | |
// Create a load more command for triggering a load more event | |
loadMoreCommand = RACCommand { _ in | |
return self.viewModel.loadMoreSignal() | |
} | |
// Load more command should not allow concurrent execution | |
loadMoreCommand.allowsConcurrentExecution = false | |
// Configure the load more signals | |
loadMoreExecuting = loadMoreCommand.executing | |
loadMoreSuccess = loadMoreCommand.executionSignals.flattenMap { $0 as RACSignal } | |
loadMoreError = loadMoreCommand.errors | |
} | |
func setupNetworkingSignals() { | |
// Setup the refresh command and signals | |
setupRefreshSignals() | |
// Setup the load more command and signals | |
setupLoadMoreSignals() | |
// Create a merged signal of `refreshExecuting` and `loadMoreExecuting` | |
executingNetworkRequest = RACSignal.merge([refreshExecuting, loadMoreExecuting]) | |
// Create a merged signal of `refreshSuccess` and `loadMoreSuccess` | |
networkingComplete = RACSignal.merge([refreshSuccess, loadMoreSuccess]) | |
// Create a merged signal of `refreshError` and `loadMoreError` | |
networkingError = RACSignal.merge([refreshError, loadMoreError]) | |
// Create a signal for when networking completes | |
// or errors, mapping it to the value of `viewModel.isEmpty` | |
isEmptySignal = RACSignal | |
.merge([networkingComplete, networkingError]) | |
.map { _ in | |
return self.viewModel.isEmpty | |
} | |
// Automatically hide the table view and show `emptyStateView` | |
// if `emptyStateView` != nil | |
isEmptySignal.subscribeNextAs { (isEmpty: Bool) in | |
if let emptyStateView = self.emptyStateView { | |
self.tableView.hidden = isEmpty | |
emptyStateView.hidden = !isEmpty | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment