Last active
April 10, 2017 03:39
-
-
Save jshier/3e5451d57911265c4dee679f1d63145a to your computer and use it in GitHub Desktop.
A stateful view controller
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
// | |
// ViewController.swift | |
// ArchitectureTest | |
// | |
// Created by Jon Shier on 6/11/16. | |
// Copyright © 2016 Jon Shier. All rights reserved. | |
// | |
import Alamofire | |
import Argo | |
import UIKit | |
class ViewController: UIViewController, ViewStateful { | |
var viewStates = ViewStates(loadingView: .blueColor(), errorView: .greenColor(), emptyView: .purpleColor()) | |
var state = ViewControllerState<HTTPBinResponse>.Loading { | |
didSet { | |
print(state) | |
} | |
} | |
override func viewWillAppear(animated: Bool) { | |
super.viewWillAppear(animated) | |
fetchValue() | |
} | |
@IBAction func refreshValue(sender: UIButton) { | |
refreshValue() | |
} | |
} | |
enum ViewControllerState<T> { | |
case Loading | |
case Loaded(state: ViewControllerLoadedState<T>) | |
case Error(error: APIError) | |
var value: T? { | |
switch self { | |
case let Loaded(state): | |
return state.value | |
default: | |
return nil | |
} | |
} | |
var error: APIError? { | |
switch self { | |
case let Loaded(state): | |
return state.error | |
case let Error(error): | |
return error | |
default: | |
return nil | |
} | |
} | |
} | |
enum ViewControllerLoadedState<T> { | |
case Content(value: T) | |
case NoContent(value: T) | |
case Refreshing | |
case Error(error: APIError) | |
var value: T? { | |
switch self { | |
case let Content(value): | |
return value | |
case let NoContent(value): // Will be able to remove duplicate return in Swift 3! | |
return value | |
default: | |
return nil | |
} | |
} | |
var error: APIError? { | |
switch self { | |
case let Error(error): | |
return error | |
default: | |
return nil | |
} | |
} | |
} | |
protocol Stateful: AnyObject { | |
associatedtype ValueType | |
var state: ViewControllerState<ValueType> { get set } | |
} | |
protocol NetworkLoadable { | |
static var route: APIRouter { get } | |
} | |
extension ViewStateful where Self: UIViewController, ValueType: NetworkLoadable, ValueType: Decodable, ValueType == ValueType.DecodedType, ValueType: ContentAware { | |
func fetchValue() { | |
switch state { | |
case .Loading: | |
view.backgroundColor = viewStates.loadingView | |
case .Loaded: | |
view.backgroundColor = .whiteColor() // reset to default to simulate normal view | |
case .Error: | |
view.backgroundColor = viewStates.errorView | |
} | |
APIManager.fetch { (response: Response<ValueType, APIError>) in | |
switch response.result { | |
case let .Success(value): | |
self.state = .Loaded(state: (value.hasContent) ? .Content(value: value) : .NoContent(value: value)) | |
case let .Failure(error): | |
self.state = .Error(error: error) | |
} | |
switch self.state { | |
case .Loading: | |
self.view.backgroundColor = self.viewStates.loadingView | |
case let .Loaded(state): | |
switch state { | |
case .NoContent: | |
self.view.backgroundColor = self.viewStates.emptyView | |
default: | |
self.view.backgroundColor = .whiteColor() | |
} | |
case .Error: | |
self.view.backgroundColor = self.viewStates.errorView | |
} | |
} | |
} | |
func refreshValue() { | |
state = .Loaded(state: .Refreshing) | |
APIManager.fetch { (response: Response<ValueType, APIError>) in | |
switch response.result { | |
case let .Success(value): | |
self.state = .Loaded(state: (value.hasContent) ? .Content(value: value) : .NoContent(value: value)) | |
case let .Failure(error): | |
self.state = .Loaded(state: .Error(error: error)) | |
} | |
switch self.state { | |
case let .Loaded(state): | |
switch state { | |
case .NoContent: | |
self.view.backgroundColor = self.viewStates.emptyView | |
default: | |
self.view.backgroundColor = .whiteColor() | |
} | |
default: | |
self.view.backgroundColor = .whiteColor() | |
} | |
} | |
} | |
} | |
extension HTTPBinResponse: NetworkLoadable { | |
static var route: APIRouter { | |
return .Default | |
} | |
} | |
protocol ViewStateful: Stateful { | |
var viewStates: ViewStates { get } | |
} | |
struct ViewStates { | |
let loadingView: UIColor | |
let errorView: UIColor | |
let emptyView: UIColor | |
} | |
protocol ContentAware { | |
var hasContent: Bool { get } | |
} | |
extension HTTPBinResponse: ContentAware { | |
var hasContent: Bool { | |
return false | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment