Refactoring OOP to Protocol Oriented Programming - ios

I have FirstViewController which has some IBOutlets, functions and presenter. Then I have SecondViewController which has all same functions as FirstViewController but with one additional function, IBOutlets and presenter exactly same as presenter of FirstViewController, but with 1 additonal functions.
I have read about OOP and POP and people recommend to use POP.
But my question is if in this situation when I need to have SecondViewController with all same functions and IBOutlets as in FirstViewController, inheritance is not better than protocols?
If I want to use protocol, how should it to be done if I want to avoid rewrite same code? With protocols default implementation?
With inheritance
class FirstViewController: UIViewController {
#IBOutlet weak var firstButton: UIButton!
#IBOutlet weak var secondButton: UIButton!
...
...
func firstFunction() {
...
}
func secondFunction() {
...
}
}
class SecondViewController: FirstViewController {
#IBOutlet weak var additionalButton: UIButton!
...
...
func additionalFunction() {
...
}
}
With default implementation
protocol FirstViewControllerProtocols {
func firstFunction()
func secondFunction()
}
extension FirstViewControllerProtocols {
func firstFunction() {
print("do something")
}
func secondFunction() {
print("do something")
}
}
and then
class FirstViewController: UIViewController, FirstViewControllerProtocols {
#IBOutlet weak var firstButton: UIButton!
#IBOutlet weak var secondButton: UIButton!
}
protocol SecondViewControllerProtocol {
func thirdFunction()
}
extension SecondViewControllerProtocol {
func thirdFunction() {
print("do something new")
}
}
class SecondViewController: UIViewController, FirstViewControllerProtocols, SecondViewControllerProtocol {
#IBOutlet weak var additionalButton: UIButton!
}
But with protocol approach I can't inherit iboutlets.

Your example of POP is not correct. The point is just to extract the shared code into protocols, not duplicate things unnecessarily. So FirstViewControllerProtocols makes sense, but there's absolutely no reason for SecondViewControllerProtocol. What second type conforms to SecondViewControllerProtocol? If you get rid of that, you'll see that most of your problems go away.
But not all of them. You're also talking about IBOutlets, which are not just a matter of shared code. If SecondViewController really is a kind of FirstViewController (rather than just "happens to share a few buttons"), then inheritance might make sense. If it's just that there are a few related IBOutlets, duplicate the IBOutlets since they're related to the type. That's not inheritance.
Given your description, you're thinking in terms of "I have a lot of code that happens to be kind of similar, and so I should make a thing to abstract it." That's not the approach; if they're just "kind of similar" then ask yourself if you're creating abstractions for no purpose. If FirstViewController were changed, would SecondViewController necessarily also change? If not, they're not actually very related. "DRY" (Do not Repeat Yourself) is about concepts, not keystrokes.
The approach is "I have two things that are fundamentally similar, will change in similar ways over time, and have algorithms that should apply to both, so I should extract a protocol."

Subclassing one view controller class from another custom view controller (as well as using default implementations in protocols) seems so elegant at first glance, but nine times out of ten, it is a mistake. If it is an abstract “base view controller”, for some common behavior, that can sometimes bear some utility (though depending upon what this shared behavior is, there are often better patterns).
But if these two view controller classes are used for different scenes in your storyboard (and your reference to sharing IBOutlets suggests that this is the case), it becomes, in my experience, a maintenance nightmare. I used this pattern in a few early projects, was quite pleased with the elegance of my code at the time, but when I was later modifying and enhancing the respective view controllers, I regretted having two view controller classes for two different scenes so tightly entangled.
Nowadays, we strive for view controller classes that are as thin and dumb as possible. (See Dave DeLong’s A Better MVC posts or the video. It is also one of the many motivating factors behind MVVM and related patterns.) View controllers are solely intended for configuring views and initiating behaviors upon user interaction or other system events. Anything beyond that should generally be abstracted out of the view controller. And if there are common UI elements in various view controllers, we will often consider view controller containment to abstract the shared UI to a third view controller which becomes a child of the first two.
So rather than POP vs OOP, ask yourself whether all this shared behavior belongs in either one of these two view controllers at all. If the shared behaviors are various UIKit delegate methods (e.g. table data source, collection view delegate methods, etc.), that calls for one solution. If they are business logic, it calls for a different solution. If they are utility methods, yet another. If it is a common set of subviews, again, yet another pattern. It just depends.

Related

Why are the outlets by default declared as weak?

I looked up this topic on google but have not got an understandable answer yet, the problem is that I know that when two classes are coupled together by instantiating an object in the first class from the second class and declaring another object in the second class from the first class this will cause a retain cycle that should be broken by using the keyword weak or unowned , yet I can not apply this way of thinking on the IBOutlets being declared as weak
for example
class SignUpViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBOutlet weak var signUpBttn: UIButton!
}
this is an outlet in my viewController class, why the outlet is declared as weak ? as per what I understand is that to have a retain cycle, the uibutton class should have an object from the viewController class so that the two classes (viewController and uibutton) become coupled together
can anybody clarify what is happening under the hood?
all ui element in a view controller are part of the viewController view. so view is a UIView class and outlets are reference in the viewController to UIView class elements, so if the view is removed form the view hierarchy all cross element should be weak to avoid cross reference. thew eid problem about this is the reference from apple MVC, where a ViewController is a Controller but have a bunch o related code of the view part. all you outlets should be placed in the UIView class of you ViewController.
TLDR - in my opinion it's not a good decision.
A strong reference denotes ownership. The view or view controller is the owner of the views therefore the logical choice should be strong.
However, in most situations this does not really matter because the views are also strongly referenced by their parent in the view hierarchy.
When does it matter? It matters in situations when you are dynamically updating the view hierarchy by removing views/constraints etc. Once you remove a view/constraint from the hierarchy and you don't have a strong reference to it, it will get removed from memory.
Also note that the combination of weak and ! is a bit dangerous because ! denotes a reference that you expect never to be nil.
This can lead to errors, for example:
#IBOutlet weak var constraint: NSLayoutConstraint!
...
constraint.isActive = false // removes constraint from hierarchy, assigns `nil` to constraint
...
constraint.isActive = true // crashes the app
Personally, I always make outlets strong. For any weak references I always use ? and not !.
Note that weak in this case doesn't have anything to do with protection against reference cycles. It was just a personal decision by Xcode developers.
Historically, there might be a connection with UIViewController.viewDidUnload. However, that method is never called since iOS 6.
Apple recommends that an #IBOutlet be declared as strong. Many discussions / articles can be found about this.
If you take a look at this video from Apple's 2015 WWDC, right around the 32:30 mark: https://developer.apple.com/videos/play/wwdc2015/407/
He states:
In general you should make your outlet strong, especially if you are connecting an outlet to a sub view or to a constraint that's not always going to be retained by the view hierarchy. The only time you really need to make an outlet weak is if you have a custom view that references something back up the view hierarchy and in general that's not recommended.

Swift confine protocol to certain types of class only

I've got a protocol that I only assign to SKScene objects. It's basically so that I can enforce certain properties and methods allowing me to create a manager class knowing that my scenes can all do certain things. e.g So I can create a tutorial scene for instance and a level 1, level 2 etc.
protocol GameSceneProtocol {
func performAction()
}
So now in my manager I have a weak var for the scene (The manager is initialised in the scene)
weak var scene: SKScene?
The issue I've got is that I want to call methods that relate to SKScene but also to my protocol. e.g
scene.addChild(modal)
scene.performAction()
What I really want to do is something like:
weak var scene: SKScene, GameSceneProtocol
The reason I'm using this manager is because I have a need to add different modal views to my scenes (again tutorials and levels) and then run common code based on actions taken in these modals. These modals and functionality are the same across the different scenes which is why I tried to move them to separate classes.
Is there a way I can ensure a property is of a certain type but also conforms to a protocol?
I've got a feeling my design is wrong up front here and there's probably a better way I should structure this. Possibly creating a base scene class that my actual scenes inherit from rather than a protocol. That way I can avoid duplicating the same code.
What you want is a subclass
class SKSceneWithProtocol : SKScene, GameSceneProtocol
but at this point the protocol is essentially pointless, and you may as well just do the code in the subclass instead.
Your other option is to cast the scene to the protocol when you need to use it
if let sceneProtocol = scene as? GameSceneProtocol
{
//do stuff here
}
You can use protocol composition to define a property where the object must be both an SKScene and a GameSceneProtocol. Since protocol compositions only work with protocols (not classes), you must also define an empty SKSceneProtocol and add that conformance to SKScene via an extension.
protocol SKSceneProtocol { }
extension SKScene: SKSceneProtocol { }
protocol GameSceneProtocol { ... }
var object: (SKSceneProtocol & GameSceneProtocol)? = nil

Creating Controllers for the UIViews found in a UIViewController

In the iOS world, we're all used to the following pattern:
class UIViewController {
open var view: UIView!
}
A ViewController is obviously a controller controlling the view, which contains a lot of subviews.
Now, I have a lot of subviews that I want to reuse, and I want to enrich them with more functionalities. Think of a UISlider, a UILabel, or a UITableView that react to some events or some changes in the model. These subviews also need to be #IBDesignable with IBInspectable properties for customisation purposes. I also want to share those components through a library as well.
So in a way, I want small controllers controlling those subviews that will end up in the view of the ViewController.
I am thinking of doing this for the UIKit classes:
#IBDesignable
public class CustomSlider: UISlider {
}
That is a nice way to be able to provide the component with customisation options. The downside is that we're using inheritance here (would rather use composition), and I'm not sure if CustomSlider is really considered here a controller or not.
Can anyone tell me what are good practices around creating controllers for subviews that are customisable? Thanks in advance!
EDIT: Specific case for Views that have delegates and datasource:
#objc public class CustomTableView: UITableView, UITableViewDataSource, UITableViewDelegate {
#IBInspectable public var someCustomField: UInt = 0
public override init(frame: CGRect, style: UITableViewStyle) {
super.init(frame: frame, style: style)
dataSource = self
delegate = self
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
dataSource = self
delegate = self
}
// Implement UITableViewDataSource and UITableViewDelegate
}
Is this bad pattern to have datasource = self and delegate = self, or is it ok?
It's a judgement call. I'd say if all you are doing is adding features to a single view class, it is probably better to just use a custom subclass like your CustomSlider example.
If, on the other hand, you're using small suites of objects, like a slider, a text field, a segmented control and a couple of labels, you might want to think about using container views, embed segues, and custom view controllers. Think of that as setting up tiles that manage sets of UI elements. You can put such a view controller anywhere you want, and size it as needed.
You could also create a custom controller object that manages one or more custom views, but there isn't really system "plumbing" for that, so the burden is on you to build mechanisms to support that approach. You have to teach your view controllers to talk to a controller object that isn't a view, but that HAS views inside it.
Apple does what you're talking about in a couple of instances: UITableViewController and UICollectionViewController, and I think they didn't do it right. A UITableViewController or UICollectionViewController can manage only a single view. You can't put a button at the bottom, or a label somewhere, or a segmented control. The content view of one of those specialized view controllers must be the corresponding view object, which limits their usefulness. You can, of course, get around the problem by using container views and embed segues, and that leads me to my suggestion.
EDIT:
As far as making a view object it's own data source, I would call that "a violation of the separation of powers". A table view is a view object, and the data source is a model object. By combining them, you're collapsing the view and the model into one. That's going to create a larger, very specialized object that is less likely to be reusable.
The same goes for making an object it's own delegate. The idea of the delegate pattern is to be able to leave certain decisions about an object's behavior up to somebody else, so that its more flexible. By making it its own delegate you are limiting the table view's range of behavior and making it less reusable.
Neither thing is going to "warp your mind, curve your spine, and make the enemy win the war" but they seem ill-advised.

Swift: How to handle tightly coupled views?

I have a UIViewController that is part of a UINavigationController and I want a custom view in my navigation bar. The code is then something like this:
class MyViewController: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
self.customNavigationView = MyNavigationView()
self.navigationItem.titleView = self.customNavigationView
}
}
MyViewController and MyNavigationView are tightly coupled in a sense - they form a single piece of UI and only make sense when used together. I wonder what the preferred way to handle such cases is pattern-wise. In particular, what's the preferred way to send messages from MyNavigationView to MyViewController (such as buttons tapped).
I saw the following options:
Use a delegate pattern, create a MyNavigationViewDelegate protocol and add a delegate property to MyNavigationView. While the most object-oriented approach, it seems a bit "over-engineered" to me and has a lot of overhead for something that is basically a single unit of UI
Make MyNavigationView an inner class of MyNavigationController to indicate their strong relationship. Seems fine, but I like to have a file per class for easy navigation
Use a weak var myViewController: MyViewController? in MyNavigationView. I don't think the variable should be an optional, though, because semantically it is not
Use a let myViewController: MyViewController and a custom initializer in MyNavigationView that sets it. Seems the best option to me right now, but I'm not sure if it can create a memory leak since its a strong reference cycle
Thanks to #Mohsen Hossein pour: Sending notifications from MyNavigationView to MyViewController.
I wonder what other people's thoughts on this are. Is there are clear pattern that should be used here or is it a matter of taste?
While any answer to this question is ultimately opinionated in the MVP design pattern a view should not contain any business logic, just display logic. which means it should not know anything about its host.
The way I would approach it is to write the connection code and the interaction code in its host instead.
class MyNavigationView : UIView
{
var button : UIButton!
}
class ViewController : UIViewController
{
override func viewDidLoad() {
super.viewDidLoad()
self.customNavigationView = MyNavigationView()
self.navigationItem.titleView = self.customNavigationView
// here you can assign gesture recognizers or add event listeners
}
}
I would use a notification if you only need to know if something happened in the other view, it is easier and faster to implement, if you need more explanation on how to use notifications please ask and I can edit my answer and explain some more.

What would be better strategy for IBActions in protocols in Swift?

I am creating a Swift project and I want to define a specific protocol that enforces other components to implement a animate method:
protocol AnimatableBehavior {
#IBAction func animate()
}
The problem is I want this method to be an IBAction, but I get this error from XCode:
Only instance methods can be declared 'IBAction'
My question is, how would you implement such a thing?
I have considered:
Remove #IBAction, but then I need to remember adding it in every class that implements. Not very elegant and error prone.
Create a base class instead of protocol, but then I am enforcing all components to subclass my base class instead of their own choice ones, so it is not a valid option.
Any other ideas?
EDIT: Response to comments below.
The idea of the IBAction on the protocol is because in the project there will be many different devs implementing small UI components, all of which have the animate method. The components can be added programatically or by Interface Builder and it is very convenient that they are always IBAction because I plan to compose them from IB files to simplify the View Controllers to the maximum extent (and this is clearly a View only task).
Therefore, the solution proposed below of adding a method in the controller that just calls the animate of the component is not good because it is redundant code and makes your Controller more dependent on your View.
The idea of letting the dev to remember adding the IBAction keyword on the method is workable, but as I said it is error prone (and by that I mean that there will be some forgetting about it), and I want to make sure that this is always accessible from IB. It also adds extra cognitive load, because I will need to document this lack of IBAction on the protocol and request the implementor to add it manually.
I know is not the common way of working in iOS and UIKit, but that was why I posted the question, maybe someone has an alternative idea.
It doesn't make any sense to have an #IBAction in a protocol. #IBAction is nothing more than a keyword for Interface Builder to have a hook when you're control+dragging from Interface Builder to your actual source code.
This is just a simple misunderstanding of what #IBAction actually is and does.
A method does not have to be marked as #IBAction in order for it to be the target of a UI element's actions. You programmatically hook up any method to any action using the addTarget set of methods that UI elements have. The method does not have to be marked as an #IBAction to do this.
Regardless of whether or not a protocol defines a method as #IBAction, the class conforming to the protocol can add it (and still be conforming to the protocol.
protocol FooProtocol {
func doSomething()
}
class ViewControllerA: UIViewController, FooProtocol {
#IBAction func doSomething() {
// do something
}
}
class ViewControllerB: UIViewController, FooProtocol {
func doSomething() {
// do something
}
}
Both of these view controller subclasses conform to the protocol, and having #IBAction there is ONLY necessary if you intend to hook up an action from interface builder!
Ultimately, whatever you're trying to do, if you think an #IBAction is necessary in your protocol, I think you're taking the wrong approach to something. It's hard to say what the right approach would be without knowing more details about what you're actually doing, but it never makes sense for #IBAction to belong in a protocol.
To me, it seems like the methods your protocol enforces shouldn't at all be tied to #IBAction methods. Instead, whatever user interaction should trigger the animation, should in turn call the animate method. For example, if we weren't talking about the protocol, my recommendation would be this sort of set up:
class ViewController: UIViewController {
#IBAction func buttonThatStartsAnimation {
self.animate()
}
func animate {
// code that does all the animation
}
}
So, with the protocol, we should take the same seperation of duties between the method that's actually initiating the animation code (which in the case of protocols, this is obviously some other outside class), and the animate method should only ever handle doing the relevant animations.
Importantly, just as a general rule, you shouldn't be directly referring to your #IBAction methods or your #IBOutlet variables directly from outside the class which defines them.
I totally agree with OP, although until Swift 3.1 you can't really declare anything as #IBOutlet, #IBAction, #objc etc in a protocol. As a workaround, I chose to build something based on pod 'ActionKit' and wrote something like:
protocol RequiresAnimation {
var animateButton: UIButton! { get }
func enableAnimateButton()
func actionAnimate()
}
extension RequiresAnimation where Self: UIViewController {
func enableAnimateButton() {
animateButton.addControlEvent(.touchUpInside) {
self.actionAnimate()
}
}
func actionAnimate() {
// animate here
}
}
And make your view controller:
class MyViewController: UIViewController, RequiresAnimation {
#IBOutlet var animateButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
enableAnimateButton()
}
}
I wish there would be any easier approach, but so far you make need to do these 2 things manually: declaring your button as #IBOutlet and call a setup function. The reason why we need to import ActionKit is that we can't addTarget in protocol extension.

Resources