Should ViewController-Presenter-Interactor have one to one relationship - ios

I am reading about VIPER and my understanding is- generally a viewController is related to one presenter and one presenter talks to one Interactor.
But, what if we have master-details pages or list-detail pages. To display list of items, I would have one controller/Presenter to display list and another controller/presenter to display details. And FetchList and FetchDetail should belong to same interactor.
If these two presenters communicate to this interactor, they would have to implement both the methods FetchList and FetchDetail. And one of these two method's implementation would be empty.

You should have two separate VIPER modules: MainItems and DetailedItems.
Read this post (https://www.ckl.io/blog/best-practices-viper-architecture) and see how to use delegates to send data between VIPER modules. Note that FetchList and FetchDetail should belong to different interactor:
// 1. Declare which messages can be sent to the delegate
// ProductScreenDelegate.swift
protocol ProductScreenDelegate {
//Add arguments if you need to send some information
func onProductScreenDismissed()
func onProductSelected(_ product: Product?)
}
// 2. Call the delegate when you need to send him a message
// ProductPresenter.swift
class ProductPresenter {
// MARK: Properties
weak var view: ProductView?
var router: ProductWireframe?
var interactor: ProductUseCase?
var delegate: ProductScreenDelegate?
}
extension ProductPresenter: ProductPresentation {
//View tells Presenter that view disappeared
func onViewDidDisappear() {
//Presenter tells its delegate that the screen was dismissed
delegate?.onProductScreenDismissed()
}
}
// 3. Implement the delegate protocol to do something when you receive the message
// ScannerPresenter.swift
class ScannerPresenter: ProductScreenDelegate {
//Presenter receives the message from the sender
func onProductScreenDismissed() {
//Presenter tells view what to do once product screen was dismissed
view?.startScanning()
}
...
}
// 4. Link the delegate from the Product presenter in order to proper initialize it
// File ScannerRouter.swift
class ProductRouter {
static func setupModule(delegate: ProductScreenDelegate?) -> ProductViewController {
...
let presenter = ScannerPresenter()
presenter.view = view
presenter.interactor = interactor
presenter.router = router
presenter.delegate = delegate // Add this line to link the delegate
...
}
}

My understanding is that you have one view/view-controller and presenter per screen and then one interactor per use case, which probably means more than one per screen. This is good practice from the point of view of Single Responsibility Principle and therefore aids testing. But sometimes a concession is made and an interactor handles multiple uses cases.

Related

Swift: Sharing logic tied to state when composition isn't a good option

Let's say you have two classes conforming to a protocol and you want some logic to be shared between them. In languages like Java, you'd typically create an abstract class with the shared logic and make use of it in subclasses. In Swift, abstract classes aren't supported. What's the recommended approach for accomplishing this?
One answer is composition, but what if the common functionality can't be divided into smaller components in a clean and sensible way?
Another answer is to implement common functionality in the protocol itself, but what if it's heavily tied to state?
Some context:
I'm working on an iOS app in which two screens use the same view for different purposes. I'm using the MVP pattern and would like to share common logic among the two presenters. There is state involved, and there isn't really a clean way to pull shared logic into separate components since it's so closely tied to the view interface.
Here's a minimal example of this situation:
protocol View {
func doSomething()
}
class ViewController : UIViewController, View {
func doSomething() { }
}
protocol Presenter {
func tellViewToDoSomething()
}
struct Presenter1 : Presenter {
let view: View
init(withView view: View) {
self.view = view
// then do something unique to presenter 1
}
func tellViewToDoSomething() {
view.doSomething()
// then do something unique to presenter 1
}
}
struct Presenter2 : Presenter {
let view: View
init(withView view: View) {
self.view = view
// then do something unique to presenter 2
}
func tellViewToDoSomething() {
view.doSomething()
// then do something unique to presenter 2
}
}
I'm asking this as a general question rather than in terms of my current situation because I'd like to understand general approaches for sharing common logic in Swift.
I'm coming from an OOP background and it's likely that I'm fundamentally misunderstanding something, so maybe someone could enlighten me.
Could a protocol extension help here? The thing that makes protocol-oriented programming tricky from an OOP perspective, of course, is that there is no super. But nothing stops you from just calling the protocol's built-in functionality:
protocol View { func doSomething() }
protocol Presenter {
var view : View {get set}
}
extension Presenter {
func tellViewToDoSomething() {
self.view.doSomething()
}
}
struct Presenter1 : Presenter {
var view: View
func tellViewToDoSomethingAndThenSome() {
self.tellViewToDoSomething()
// and then some
}
}

How can I create an instance of a custom model with multiple viewcontrollers in Xcode with Swift 4?

I'm new to Swift and I'm sure this question is pretty basic and has been asked and answered before.
I am not using storyBoard. My main viewController is created from AppDelegate via code.
I have:
a custom class defined in a model.swift file
a main viewController (from AppDelegate) that I am using as a container
3 additional viewcontrollers as subviews of the main (not each other)
all 3 subviews are displayed simultaneously each covering 1/3 of the screen (no segues)
each viewcontroller is in a separate .swift file
I want to create an instance of my custom class in the main viewController and have all 3 of the subviews be able to reference that instance.
Each of the subview view controllers need to be able to get/set instance variables and the other subviews need to be made aware of those changes.
I think I will need to use notifications to communicate the changes to the multiple subviews - but I haven't even begun to try and figure that out yet.
If this has been asked and answered before - could someone please either provide a link - or provide me with the right search terms so that I'm able to find the answer? The only found answers I've found that come close are to use segues to pass the data back and forth.
You can use delegate pattern. Below code is assuming that you are using MVVM pattern. (It is very similar for VIPER/ReSwift patterns also)
protocol DataChangedDelegate {
func refreshData()
}
// ViewModel for FirstViewController
class FirstViewModel {
var delegate: DataChangedDelegate?
var data: Any {
didSet {
delegate?.refreshData()
}
}
//rest of the things
}
//similarly other two view models will have a delegate and on data change will call the refresh method
And your view controllers should adopt this protocol
class FirstViewController: UIViewController, DataChangedDelegate {
//view controller code
//delegate code
func refreshDate() {
//tableView.reloadDate()
//collectionView.reloadDate()
//lableView.text = viewModel.data()
}
}
And where ever you create a viewControllers and add as subView, you have to set the delegate of viewModel.
let firstViewController: FirstViewController = createFirstViewController()
let firstViewModel = FirstViewModel()
firstViewModel.delegate = firstViewController
firstViewController.viewModel = firstViewModel
mainViewController.addSubView(firstViewController.view)
Similarly for all other view controllers.
Here's how I would do it:
Create a singleton class.
Configure the singleton's properties in the the main ViewController.
Use didSet to post a Notification.
Add a listener for that Notification in your additional ViewControllers.

How to subscribe to delegate events globally?

I have a custom delegate that triggers certain events. For context, it's a bluetooth device that fires events arbitrarily. I'd like my view controllers to optionally subscribe to these events that get triggered by the device delegate.
It doesn't make sense that each view controller conforms to the custom delegate because that means the device variable would be local and would only fire in that view controller. Other view controllers wouldn't be aware of the change. Another concrete example would be CLLocationManagerDelegate - for example what if I wanted all view controllers to listen to the GPS coordinate changes?
Instead, I was thinking more of a global delegate that all view controllers can subscribe to. So if one view controller triggers a request, the device would call the delegate function for all subscribed view controllers that are listening.
How can I achieve this architectural design? Are delegates not the right approach? I thought maybe NotificationCenter can help here, but seems too loosely typed, perhaps throwing protocols would help makes things more manageable/elegant? Any help would be greatly appreciated!
You could have an array of subscribers that would get notified.
class CustomNotifier {
private var targets : [AnyObject] = [AnyObject]()
private var actions : [Selector] = [Selector]()
func addGlobalEventTarget(target: AnyObject, action: Selector) {
targets.append(target)
actions.append(action)
}
private func notifyEveryone () {
for index in 0 ..< targets.count {
if targets[index].respondsToSelector(actions[index]) {
targets[index].performSelector(actions[index])
}
}
}
}
Naturally, you'd have to plan further to maintain the lifecycle of targets and actions, and provide a way to unsubscribe etc.
Note: Also ideal would be for the array of targets and actions to be an of weak objects. This SO question, for instance, deals with the subject.
• NotificationCenter is first solution that comes in mind. Yes, it is loosely typed. But you can improve it. For example like this:
extension NSNotificationCenter {
private let myCustomNotification = "MyCustomNotification"
func postMyCustomNotification() {
postNotification(myCustomNotification)
}
func addMyCustomNotificationObserverUsingBlock(block: () -> ()) -> NSObjectProtocol {
return addObserverForName(myCustomNotification, object: nil, queue: nil) { _ in
block()
}
}
}
• Second solution would be to create some shared object, which will store all delegates or blocks/closures and will trigger them when needed. Such object basically will be the same as using NotificationCenter, but gives you more control.

Updating uitableview datasource and updating tabbar

I need your advice.
I have a screen with a table view and I'm using a separate file for UITableViewDataSource.
What I need is to be able to access the UITabBarItem inside my UITableViewDataSource (because based on the action on my tableview, I will update my tableview data, so I need to change something on my tabbar)
Any idea ?
Thanks.
To separate my datasource, in my controller I have:
var toPayDatasource: MyDataSource!
....
tableView.dataSource = mDatasource
tableView.delegate = myDatasource
And my datasource:
class MyDataSource: NSObject, UITableViewDataSource, UITableViewDelegate {
...
}
C.C.
One common mechanism for sending a message between two objects that are otherwise far apart is to use NSNotificationCenter. You can define a custom event and pass along whatever you need to update the tab bar item in the userInfo dictionary.
Since your datasource is separate you can create a Delegate for the view controller and pass it to the data source. Something like the following (right after setting data source for the tableView):
mDataSource.tabDelegate = self;
And declare / define the delegate the following way:
// protocol
protocol TabChangerProtocol {
// function signatures here
}
// data source
class MyDataSource : /* ... */ {
var tabDelegate : TabChangerProtocol!;
// rest here
}
// view controller
class ViewController : TabChangerProtocol, /* ... */ {
// ...
}
That way you can call the protocol functions from inside MyDataSource.
P.S Sorry for all the 3 dots. Typing code on mobile is annoying.

Pass data when dismiss modal viewController in swift

I'm trying to pass data from the modal ViewController to his source ViewController. I think I have to use delegation but it doesn't work.
protocol communicationControllerCamera{
func backFromCamera()
}
class Camera: UIViewController{
var delegate: communicationControllerCamera
init(){
self.delegate.backFromCamera()
}
}
class SceneBuilder: UIViewController, communicationControllerCamera{
func backFromCamera(){ // Never called
println("YEAHH")
}
}
The backFromCamera method it's not called. What did I do wrong?
You didn't set a delegate so it was empty when you tried to call backFromCamera().
Here's a simple working example you can test out. Notice the use of the optional type (?) for the delegate.
// Camera class
protocol communicationControllerCamera {
func backFromCamera()
}
class Camera: UIViewController {
var delegate: communicationControllerCamera? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.delegate?.backFromCamera()
}
}
// SceneBuilder class
class SceneBuilder: UIViewController, communicationControllerCamera {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
var myCamera = Camera()
myCamera.delegate = self
self.presentModalViewController(myCamera, animated: true)
}
func backFromCamera() {
println("Back from camera")
}
}
You can find all the information you need in Apple's Swift documentation.
Obviously the chosen answer is correct, but it didn't help me. I did successfully implement protocols though, so I wanted to provide my own explanation in case anyone is struggling with grasping the concept, like I was.
Protocol Code Is Written in Three Places:
Two ViewController Classes
The Protocol itself (code written outside of VC classes)
When I write my protocols, I put them in my "ToolBox" document and I still write comments to remind myself which VCs are doing what. Two examples:
So there is always:
The protocol code (shown above)
Code in a VC which initiates the action
Code in a VC which is delegated to carry out the action
1. The protocol code
See the image above for a reference. Essentially, the protocol code is just where you give the protocol a name and declare what functions you want to remotely call/delegate to. Name the protocol. Declare the names of the functions that can be called upon and declare their parameter types such as string, etc.
2. Code in a VC which initiates the action
This is the code that initiates the protocol. In this example, this is code from a table cell, which needs to delegate some work back to the main table VC. The first screenshot shows the creation of the delegate variable and the second screenshot is the actual use of that variable.
So the below code are table-cell buttons. They all need to trigger code outside of the cell VC, so they all trigger functions using the protocol I declared above.
3. Code in a VC which is delegated to carry out the action
Now the protocol is being called, but which VC answers the call? To answer that question, choose the VC and add the protocol name to the class declaration:
Lastly, you need the actual meat of the whole thing. Not the trigger, not the protocol itself, not the class declaration... but the actual function you want to call:
Hope This Helps
I don't know why protocols just wouldn't sink through my thick skull but they wouldn't. I hope this helps others like me!

Resources