I am trying to develop a theme engine, which loads themes from a json
. I have a Thememanager
which is a singleton
class and holds a currentTheme
variable.
I then have a baseViewController
which listens to any change in the currentTheme
with the help of Boxing
technique, and all the viewControllers need to be subclass of
base
and need to override
the observer
method to apply their styles. In the box
class I have an array of listeners
so that multiple view controllers can observer theme change simultaneously, it works well and now
my problem is that whenever a view controller gets deallocated
, I want to remove that listener also from the box class array of listeners, which I am unable to figure out, because of which listeners are getting piled up.
I tried to write an unbind
method in the deint of the viewController and tried to pass the closure like the below but it didnt work
func unbind(listener: Listener?) {
self.listeners = self.listeners.filter { $0 as AnyObject !== listener as AnyObject }
}
Thememanager
class Thememanager {
// Hold a list of themes
var themes = [Theme]()
// Private Init
private init() {
fetchMenuItemsFromJSON()
// You can provide a default theme here.
//change(theme: defaultTheme)
}
// MARK: Shared Instance
private static let _shared = Thememanager()
// MARK: - Accessors
class func shared() -> Thememanager {
return _shared
}
var currentTheme: Box<Theme?> = Box(nil)
func change(theme: Theme) {
currentTheme.value = theme
}
private func fetchMenuItemsFromJSON() {
// TRIAL
let theme = Theme()
themes.append(theme)
}
}
BOX
class Box<T> {
typealias Listener = (T) -> Void
var listeners = [Listener?]()
var value: T {
didSet {
for listener in listeners{
listener?(value)
}
}
}
init(_ value: T) {
self.value = value
}
func bind(listener: Listener?) {
self.listeners.append(listener)
for listener in listeners{
listener?(value)
}
}
func unbind(listener: Listener?) {
self.listeners = self.listeners.filter { $0 as AnyObject !== listener as AnyObject }
}
}
BaseViewController
class BaseViewController: UIViewController {
private var themeManager = Thememanager.shared()
typealias Listener = (Theme?) -> Void
var currentListener: Listener?
override func viewDidLoad() {
super.viewDidLoad()
observeThemeChange()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Bind the theme variable so that changes are immediately effective
func observeThemeChange() {
currentListener = {[weak self] (theme) in
guard let currtheme = theme else {
return
}
self?.loadWith(theme: currtheme)
}
themeManager.currentTheme.bind(listener: currentListener)
}
// This method will be implemented by the Child classes
func loadWith(theme: Theme) {
self.navigationController?.navigationBar.tintColor = theme.navigationBarTextColor
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor : theme.navigationBarTextColor]
// Need to be implemented by child classes
print("theme changed")
}
deinit {
themeManager.currentTheme.unbind(listener: currentListener)
}
}
Theme
struct Theme {
// Define all the theme properties you want to control.
var navigationBarBgColor: UIColor = UIColor.darkGray
var navigationBarTextColor: UIColor = UIColor.black
}