Reuse ViewController in Coordinator pattern, Swift - swift

I have two coordinators for Flow A and Flow B.
They looks like this:
final class HomeCoordinator: Coordinator {
var navigationController: UINavigationController
init(navigationController: UINavigationController = UINavigationController()) {
self.navigationController = navigationController
So for each coordinator I start the flow with an UINavigationController.
Lets say that coordinator with Flow A needs to display CommonViewController, but the coordinator with Flow B would also like to show the CommonViewController.
Since the coordinator is injected in CommonViewController, it can't be both CoordinatorA or CoordinatorB. So to perform coordinator operations I've added a delegate that looks like this:
protocol CommonViewControllerDelegate: AnyObject {
func showAnotherViewController()
}
class CommonViewController: UIViewController {
weak var delegate: CommonViewControllerDelegate?
But with this approach I have duplication of code because both CoordinatorA and CorodinatorB should implement showAnotherViewController method. And I have multiple view controllers like this, sometimes the delegate doesn't work properly and it's a chaos.
How can I solve this problem? I thought about having one coordinator, but I prefer to keep them separate so I can instantiate one UINavigationController per Coordinator.

I am not Swift guy, but give me a try. If you want to share the same behaviour between classes, then you can use composition or inheritance.
Let me show an example of inheritance via C#:
public class AnotherViewController
{
public string Show()
{
return "Show AnotherViewController";
}
}
public class CoordinatorA : AnotherViewController
{
}
public class CorodinatorB : AnotherViewController
{
}
and an implementation with composition would look like this:
public class AnotherViewController
{
public string Show()
{
return "Show AnotherViewController";
}
}
public class CoordinatorA
{
private AnotherViewController _anotherViewController;
public CoordinatorA()
{
_anotherViewController = new AnotherViewController();
}
}
public class CorodinatorB
{
private AnotherViewController _anotherViewController;
public CorodinatorB()
{
_anotherViewController = new AnotherViewController();
}
}
It is worth to read also this topic when to choose composiiton or inheritance.

Related

How can my Protocol oriented to specify ViewModel?

I have a problem from Protocol oriented ViewModels.
I have two very similar pages,so i made a baseController and baseViewModel to put shared properties and methods. And made two other controllers and viewModels for two pages.
And I made a protocol to define the properties and methods.
My baseController has var viewModel: BaseViewModelProtocol .
But my other two controller cannot use the properties and methods from their viewModel, it's says
Value of type 'BaseViewModelProtocol?' has no member ''
ViewModel1 is for Controller1, ViewModel2 is for Controller2, here is my example
protocol BaseViewModelProtocol {
var name: String { get }
func reset()
}
class BaseViewModel: BaseViewModelProtocol {
func reset() { }
}
class ViewModel1: BaseViewModel {
var score: Int = 0
func someMethods() {}
}
class ViewModel2: BaseViewModel {
var money: Int = 1000
func something() {
print("something")
}
}
class BaseViewController: UIViewController {
var viewModel: BaseViewModelProtocol?
init(viewModel: BaseViewModelProtocol) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
}
class ViewController1: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(viewModel?.score) //it is error!!!!
}
}
When i init the Controller, i Cannot use any properties and methods from ViewModel2, and the same like controller1.
class ViewController2: BaseOrderViewController {
override func viewDidLoad() {
super.viewDidLoad()
viewModel?.something(). //it is error!!!!
}
}
how to my protocol oriented viewModel?
After reading your post I feel like there is too many things that should be addressed here.
You are trying to ask something before understanding important characteristics of the Object Oriented Programmation paradigm. I recommend you to try and search information about class abstraction, composition vs inheritance and specificaly how to use the protocols in Swift.
Maybe you could start with https://cocoacasts.com/how-to-create-an-abstract-class-in-swift.
Even so, i am going to try to point out some issues in your code.
Seems like you tried to "hide" your source code by changing your classes and properties' names. The way it's done makes it harder to read, understand and introduces mistakes.
Inheriting from another class:
class ViewModel1: BaseViewModel
it's not the same as inheriting from a protocol:
class BaseViewModel: BaseViewModelProtocol
while the first one provides you a default implementation of a method that can be overrided, the second, doesn't. So it is mandatory to provide the implementation of the method in every class that inherits the protocol.
Note that the BaseViewController has a property with type BaseViewModelProtocol. That protocol doesn't have a method called "something" neither can inherit that function from another. It's within reason that the compiler shows the error.
If you want to use "something" from a Class that inherits BaseViewController, you have many ways. You could change the type of the viewModel in the BaseViewController to BaseViewModel after adding there an implementation of "something". You could also add the function to the protocol and make sure that it's implemented in all of the classes which inherit the protocol...
I hope I have helped you.

Correct way to add Generic types inheritance

I have created these classes:
class BaseViewModel<NavigatorType> {
typealias Navigator = NavigatorType
var navigator: Navigator!
}
class BaseViewController<ViewModel: BaseViewModel<Any>>: UIViewController {
typealias ViewModel = ViewModel
var viewModel: ViewModel!
}
class MyVC: BaseViewController<MyViewModel> {
}
class MyViewModel: BaseViewModel<MyNavigator> {
}
Now the problem is I receive this error on MyVC class:
'BaseViewController' requires that 'MyViewModel' inherit from
'BaseViewModel<Any>'
If I remove BaseViewModel<Any> from my BaseViewController generic parameter then the error goes. But I want to restrict view controller's generic ViewModel to inherit from BaseViewModel only.
Any idea how to do that?
The specialized type BaseViewModel<MyNavigator> is different from the specialized type BaseViewModel<Any>. I suspect you're picturing it like BaseViewModel<MyNavigator> can override/inherit from BaseViewModel<Any> since MyNavigator is a more specific Any. But Swift doesn't work that way. I don't know enough about the internals of Swift to know why it doesn't, but it doesn't!
Perhaps try making BaseViewModel a protocol instead:
protocol BaseViewModel {
associatedtype Navigator
var navigator: Navigator! { get }
}
class BaseViewController<ViewModel: BaseViewModel>: UIViewController {
var viewModel: ViewModel!
}
class MyVC: BaseViewController<MyViewModel> {
func foo() {
viewModel.navigator.bar()
print(viewModel.mySpecificProperty)
}
}
class MyViewModel: BaseViewModel {
var navigator: MyNavigator!
var mySpecificProperty: String = "Hello!"
}
class MyNavigator {
func bar() {
print("MyNavigator bar")
}
}
If the goal is to give view controllers a specific typed view model to play with, that should do it.

How to make self.dynamicType into a typealias

Basically I want to add a typealias to UIViewController or any other default swift classes. The reasoning behind this is that I want to abstract my code so that I can access some static functions by just using this instead of self.dynamicType
extension UIViewController {
typealias this = TheClassThatSubclassedThis
}
class DetailViewController: UIViewController {
func doStuff() {
this.doStaticStuff()
}
static func doStaticStuff() {
...
}
}
I know this is possible by creating a protocol, then just implement said protocol to the class I want to implement it to, like this
protocol CanAccessStaticSelf {
typealias this
}
class DetailVC: UIViewController, CanAccessStaticSelf {
typealias this = DetailVC
}
But is there a more efficient way to do this? Like for example, by just subclassing a certain class or by extending a superclass?
Like this for example
extension UIViewController {
public static var defaultNibName: String {
return self.description().componentsSeparatedByString(".").dropFirst().joinWithSeparator(".")
}
}
class DetailVC: UIViewController, CanAccessStaticSelf {
func doSomeStuffAgain() {
// no other code just subclass and I can access self.dynamicType as just `this`
print(this.defaultNibName)
}
}
Try this instead:
protocol CanAccessStaticSelf {
typealias this = Self
}
...but what you are trying to achieve looks somewhat confusing to me ;-(
Thanks to this proposal from Erica Sadun we all might be able to use the Self keyword for that in the near future.
For instance:
class MyClass {
static func staticMethod() { ... }
func instanceMethod() {
MyClass.staticMethod()
Self.staticMethod()
}
}
it is not possible to access through this.
but you can access through "UIViewController.defaultNibName".

Delegation on a class in Swift

I have a delegation/initialization problem I can't seem to solve. Basically I have a storyboard with a few View controllers. Inside the storyboard there is this "View controller" which consists of a UITableview that I have connected with a DeviceListViewController class so that it populates the information. In here I have declared the following protocol:
protocol DeviceListViewControllerDelegate: UIAlertViewDelegate {
var connectionMode:ConnectionMode { get }
func connectPeripheral(peripheral:CBPeripheral, mode:ConnectionMode)
func stopScan()
func startScan()
}
and inside the class itself I have a init method like this (which is probably wrong but I didn't know what else I could do at this point):
convenience init(aDelegate: DeviceListViewControllerDelegate) {
self.init()
self.delegate = aDelegate
}
Then there is this second class that is not attached to any view controller called BLEMainViewController. It should be a singleton handling all the bluetooth actions. This means I should be able to delegate some stuff between DevicelistViewController and BLEMainViewController.
In the BLEMainViewController I have inherited the DeviceListViewControllerDelegate:
class BLEMainViewController: NSObject, DeviceListViewControllerDelegate {
var deviceListViewController:DeviceListViewController!
var delegate: BLEMainViewControllerDelegate?
static let sharedInstance = BLEMainViewController()
}
override init() {
super.init()
// deviceListViewController.delegate = self
deviceListViewController = DeviceListViewController(aDelegate: self)
}
The problem is that BLEMainViewController is not attached to any View Controller (and it shouldn't IMO) but it needs to be initialized as a singleton in order to handle all the BLE actions. Can anyone point me in the right direction (with an example preferably) on how to work around this?
I think you simply used wrong code architecture.
The BLEManager is a shared-instance, you can call it from everywhere, set it properties, and call its methods.
Its can delegate your view-controller with any predefine events you will add to its protocol and provide proper implementation
Here is some code, hope it helps
protocol BLEManagerDelegate{
func bleManagerDidStartScan(manager : BLEManager)
}
class BLEManager: NSObject {
static let sharedInstance = BLEManager()
var delegate: BLEManagerDelegate?
var devices : [AnyObject] = []
func startScan(){
delegate?.bleManagerDidStartScan(self)
//do what ever
}
func stopScan(){
}
}

How can I force override methods to call super in Swift?

I have an abstract view controller that has some functions. Some view controllers will extend the abstract one and override some functions. I need to force some functions when any view controller override them, to call super.
I suggest that you make your public methods final and expose empty internal methods that do nothing in the abstract class but get called in your public final methods.
Something like this:
class AbstractViewController {
internal func willDoStuff() {
}
internal func didDoStuff() {
}
final public func doStuff() {
willDoStuff()
// Do the stuff
didDoStuff()
}
}
class RealViewController : AbstractViewController {
override internal func willDoStuff() {
print("Will do stuff")
}
}
let viewController = RealViewController()
viewController.doStuff()