First of all a small introduction, im relatively new to Swift and to programming in general, been doing it for the last year and loving every and each new thing of this vast world.
My post is about some technical advices and to know if the decisions that are being made in my company makes any sense. I will first address the design that is being suggested and next my conclusions.
The design that's being implemented;
We are working in a big app, this app has some flows where they follow a sequence of 5 to 8 controllers, our team leader insists in this design pattern; Let’s call the first controller a holder and the holder(black border) is responsible to have a container, this container has a proper navigation controller(red border), also, the holder hold all the data(orange) that those secondary controllers are generating.
Diagram of what this pattern is trying to achieve
To do this every red border controller has a method:
private func getParent() -> HolderViewController? {
if let parent = navigationController?.parent as? HolderViewController {
return parent
}
return nil
}
And to write in holder we call the method;
getParent().someModel.someModelProperty = "some data”
Conclusion;
Passing data through navigation controller seems to go against to the single responsibility principle. Creating strong references in every controller, even if I ensure that the navigationController is properly deinit when flow ends, does not seem a good option, this could lead to memory leaks and retain cycles. I cannot ensure that, for some hod reason, two controllers could try to access the same property at the same time. This seems the Singleton Design pattern but with a limited “scope”
Resolutions;
Since I am new and I’m working in a company, and, every company has a hierarchy my objective above all is to learn if my conclusions are wrong and have better and more concise explanation about this.
First of all, to address the problem of memory leaks I created a concurrentQueue. Instead of accessing the model directly to write in it I tried to address it through a method that will use a keyPath instead of the model directly, this is the method I’m using to write in the model;
In holder:
class HolderViewController: UIViewController {
@IBOutlet weak var bottomNavigationContainer: UIView!
private var bottomNavigationController: UINavigationController!
private var someModel: SomeModel!
private let concurrentQueue: DispatchQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
override func viewDidLoad() {
super.viewDidLoad()
setupBottomNavigation()
}
private func setupBottomNavigation() {
rootController = SecondayViewController()
if let root = rootController {
bottomNavigationController = UINavigationController(rootViewController: root)
addChild(bottomNavigationController)
bottomNavigationController.view.frame = bottomNavigationContainer.bounds
bottomNavigationContainer.addSubview(bottomNavigationController.view)
}
}
}
extension HolderViewController {
public func setValueInModel<Value>(_ value: Value, forKey path: WritableKeyPath<SomeModel, Value>) {
concurrentQueue.async(flags: .barrier) { [weak someModelInstance] in
if var obj = someModelInstance {
obj[keyPath: path] = value
}
}
}
public func readFromHolder() -> SomeModel {
concurrentQueue.sync {
return self.someModelInstance
}
}
}
The red border controllers are the ones inside the bottom navigation controller;
class RedBorderViewController: UIViewController {
var someString: String?
@IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let text = readFromHolder() {
textField.text = text
}
}
@IBAction func continueButton(_ sender: Any) {
if let text = textField.text {
setValueInHolder(text)
}
}
private func getParent() -> HolderViewController? {
if let parent = navigationController?.parent as? HolderViewController {
return parent
}
return nil
}
private func setValueInHolder(string: String) {
getParent().setValueInModel(string, forKey: \.someModelProperty)
}
private func readFromHolder() -> String {
return getParent().readFromHolder().someModelProperty
}
}
the code above is just a example of how we doing things
This look like some messy code to do a simple thing, we could use closures, delegates, segues etc… but my team leader does not like the other, simpler and more common solutions. Forgot to mention, every of our controllers has a xib and we do not use storyboards. I know the basics of how to use the other options, what I’m trying is to understand if this is or it isn’t a good solution and why, and if my way of thinking and my methods make any sense.
Remember, every conclusion I took or every solution I've implemented could be wrong, that’s why I’m sharing with you my thoughts in order to learn from your advices and experience
Thanks in advance. :) Stay safe!