Reputation: 3035
I have a BaseViewController
which is subclassed by all other view controllers in my app.
I have some state variables which must be consistent across all view controllers, so I plan to write code for passing back-and-forth these state variables once in the BaseViewController
.
For this I'm providing a helper function pushStatefulViewControllerWithIdentifier()
for forward passing and using StatePassBackDelegate
for backward passing.
import UIKit
class BaseViewController: UIViewController, StatePassBackDelegate {
class State {
var connected = false
var loggedIn = false
}
var state = State()
weak var delegate: StatePassBackDelegate? = nil
// MARK: Lifecycle
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if self.isMovingFromParentViewController() == true {
delegate?.passBackState(state)
}
}
// MARK: StatePassBackDelegate functions
func passBackState(state: AnyObject) {
self.state = state as! State
}
// MARK: Helpers
final func pushStatefulViewControllerWithIdentifier(identifier: String) {
let vc = storyboard?.instantiateViewControllerWithIdentifier(identifier) as! BaseViewController
vc.state = state
vc.delegate = self
navigationController!.pushViewController(vc, animated: true)
}
}
protocol StatePassBackDelegate: class {
func passBackState(state: AnyObject)
}
Upvotes: 0
Views: 72
Reputation: 2229
I don't see retain cycle in your code. If you afraid that some object is not destroying when it's expected, you can log its deallocation to check:
deinit {
print("deinit of \(self)")
}
Agree, that you rather need to introduce some Model level entity for state caring instead of having it on the Controllers layer. Singleton is ok if you don't plan to cover related logic with unit tests.
EDITED
You can store shared objects in app delegate.
Not sure that I fully understand your app object model. I assumed that the state should be shared across all the app, so better to inject some SystemStateMachine into all interested components. Hope it helps:
@UIApplicationMain
class MyAppDelegate: UIResponder, UIApplicationDelegate {
var controllersFactory: IViewControllersFactory! = nil
func applicationDidFinishLaunching(application: UIApplication) {
let storyboard = UIStoryboard(name:NSBundle.mainBundle().infoDictionary!["UIMainStoryboardFile"] as! String , bundle: NSBundle.mainBundle())
controllersFactory = ViewControllersFactory(storyboard: storyboard, stateMachine: SystemStateMachine())
}
}
struct SystemState : OptionSetType {
let rawValue: Int
static let None = SystemState(rawValue: 0)
static let Connected = SystemState(rawValue: 1 << 0)
static let LoggedIn = SystemState(rawValue: 1 << 1)
}
protocol ISystemStateMachine {
var currentState: SystemState { get set }
}
class SystemStateMachine: ISystemStateMachine {
var currentState: SystemState = .None
}
protocol IViewControllersFactory {
func instantiateViewController(identifier: String) -> BaseViewController?
}
class ViewControllersFactory: IViewControllersFactory {
let _storyboard: UIStoryboard
let _stateMachine: ISystemStateMachine
init(storyboard: UIStoryboard, stateMachine: ISystemStateMachine) {
_storyboard = storyboard
_stateMachine = stateMachine
}
func instantiateViewController(identifier: String) -> BaseViewController? {
if let vc = _storyboard.instantiateViewControllerWithIdentifier(identifier) as? BaseViewController {
vc.stateMachine = _stateMachine
return vc
}
return nil
}
}
class BaseViewController: UIViewController {
var stateMachine: ISystemStateMachine!
// MARK: Lifecycle
func somethingWorthHappen() {
//switch state
stateMachine.currentState.insert(.Connected)
}
func somethingWorth2Happen() {
//switch state
stateMachine.currentState.insert(.LoggedIn)
guard let appDelegate = UIApplication.sharedApplication().delegate as? MyAppDelegate else {
//log an error
return
}
if let vc = appDelegate.controllersFactory.instantiateViewController("myViewController") {
navigationController!.pushViewController(vc, animated: true)
}
}
}
Upvotes: 1