kerimdeveci
3/4/2020 - 6:31 PM

Swift State Pattern

Swift State Pattern

import Foundation

fileprivate protocol Statelike {
    var stateMachine: StateMachine { get }
    func logIn()
    func logOut()
}

extension Statelike {
    func logIn() {}
    func logOut() {}
}

fileprivate struct LoggedIn: Statelike {
    var stateMachine: StateMachine
    
    func logOut() {
        stateMachine.setState(LoggedOut(stateMachine: stateMachine))
        stateMachine.loggedOut?()
    }
}

fileprivate struct LoggedOut: Statelike {
    var stateMachine: StateMachine
    
    func logIn() {
        stateMachine.setState(LoggedIn(stateMachine: stateMachine))
        stateMachine.loggedIn?()
    }
}

class StateMachine {
    typealias StateHandler = () -> ()
    var loggedIn: StateHandler?
    var loggedOut: StateHandler?
    
    private lazy var state: Statelike = {
        return LoggedOut(stateMachine: self)
    }()

    @objc func logIn() {
        state.logIn()
    }
    
    @objc func logOut() {
        state.logOut()
    }
    
    fileprivate func setState(_ state: Statelike) {
        self.state = state
    }
}
import UIKit

protocol Stateful {
    func loggedIn()
    func loggedOut()
}

extension UIViewController: Stateful {
    func loggedIn() {
        navigationItem.loggedIn()
    }
    
    func loggedOut() {
        navigationItem.loggedOut()
    }
}

extension UINavigationItem: Stateful {
    func loggedIn() {
        rightBarButtonItem = UIBarButtonItem(title: "Logout", style: .plain, target: nil, action: nil)
    }
    
    func loggedOut() {
        rightBarButtonItem = UIBarButtonItem(title: "Login", style: .plain, target: nil, action: nil)
    }
} 
import UIKit

extension UIBarButtonItem {
    func addTarget(_ target: AnyObject?, action: Selector){
        self.target = target
        self.action = action
    }
}

extension ViewController {
    override func loggedIn() {
        super.loggedIn()
        self.navigationItem.rightBarButtonItem?.addTarget(self.stateMachine, action: #selector(StateMachine.logOut))
    }
    
    override func loggedOut() {
        super.loggedOut()
        self.navigationItem.rightBarButtonItem?.addTarget(self.stateMachine, action: #selector(StateMachine.logIn))
    }
}

class ViewController: UIViewController {
    
    var stateMachine = StateMachine()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        stateMachine.loggedIn = { self.loggedIn() }
        stateMachine.loggedOut = { self.loggedOut() }
        stateMachine.logIn()
    }
}