Is it somehow possible to have a protocol with a default implementation via protocol extension to get some code automatically executed by just implementing the protocol in various places?
Heres an example of what I mean:
protocol Greetable {
func greet()
}
extension Greetable where Self: UIViewController {
func greet() {
print("Hello ViewController")
}
func viewDidLoadMagicFunctionHookIdontKnowIfItIsPossible() {
greet()
}
}
class MyViewController: UIViewController, Greetable {}
So I want to define a protocol, give it default implementations and then hook the extension to let's say viewDidLoad() or other functions without explicitly calling the greet function by myself in every instance.
Subclassing is not possible since I want to use UITableViewController, UIViewController, UINavigationController, UISplitViewController, etc. and I cannot create one generic baseclass for all of them at once.
I also don't want to go through all of my view controllers and call greet() in viewDidLoad() because what if I decide to move the greet() to viewDidAppear? I had to go through all the controllers and move the code.
By the way, the order of when this magic function would be called would not matter at all for me, in case I have two protocols with this magic.
I'm surely not the only one who thought about something like this and there are programmers a thousand times smarter than me who surely can exactly explain why and how or why it is not possible in Swift to "hook" yourself into a certain function call.
Related
I have two main screens in my app, currently both just subclasses of UIViewController. These two view controllers are very similar - they both implement my custom subclass of UIView called HeaderView that is responsible for displaying information and taking user input. As it stands, this code is repetitive because the HeaderView setup is the same for both view controllers - the only difference is what happens when the user confirms the text entry in HeaderView.
To cut down on repetitive code, I am creating a class called InputViewController (a subclass of UIViewController) that houses the aspects of the two view controllers that are identical. Eventually, I want the two view controllers to subclass InputViewController instead of UIViewController.
class InputViewController: UIViewController, InputProtocol {
private let headerView = HeaderView()
override func viewDidLoad() {
super.viewDidLoad()
// layout, etc.
setupCallbacks()
}
internal func setupCallbacks() {
headerView.onUpdate = { (text: String) in
// called when user confirms text entry in headerView
self.onHeaderUpdate()
}
}
internal func onHeaderUpdate() {} // Blank function
}
setupCallbacks() and onHeaderUpdate() are methods defined in the protocol that the InputViewController conforms to. The HeaderView implements a callback closure that is handled in setupCallbacks() by headerView.onUpdate...
The protocol that InputViewController conforms to:
protocol InputProtocol {
func setupCallbacks()
func onHeaderUpdate()
}
To illustrate this, I drew up a diagram;
Since I want the subclasses of InputViewController to override the onHeaderUpdate() method, is it conventional to leave the definition of onHeaderUpdate() in InputViewController blank or is there another solution to this?
is it conventional to leave the definition of onHeaderUpdate() in InputViewController blank
Yes, that is called an abstract method. It is common to give it code that crashes deliberately, as a way of saying, “I exist only to be overridden in a subclass.”
(I should go further and say that what you are creating, a base view controller that carries out initial configurations that all subclasses must implement, is also normal.)
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
}
}
I'm trying to create a protocol that I can use on UIViewControllers that will do some setup work when the protocol is attached to a UIViewControler. I currently have the following code.
protocol MyProtocol {
}
extension MyProtocol where Self: UIViewController {
func setup() {
print("We have successfully setup this view controller")
}
}
class MyViewController: UIViewController, MyProtocol {
override func viewDidLoad() {
super.viewDidLoad()
print("Other setup work here") // This line might or might not exist
setup() // Goal is to remove this line, so I don't forget to add it across all my view controllers
}
}
My question is, is there a way to remove the setup() call from within the viewDidLoad function? I think it'd be a lot safer to not have to call that function every time. If there is a view controller that forgets to add that one call, then the setup won't happen, and I want to try to prevent that.
There is a chance that the viewDidLoad function on the view controller that it is attached to will do other work (ex. in this example print("Other setup work here")), or there is a chance it won't do anything except for that setup call.
I'm also not completely opposed to moving that setup function call into a separate function within the view life cycle, but again those other functions in the view life cycle that get called might have other things that need to run as well, so I don't want to completely override them.
I have also considered using the init method somehow, but I think the problem with that is that the view won't have been loaded yet and therefor I can't do the proper setup work like changing a label's text and such.
I'm having a lot of fun learning iOS dev and Swift, but having a spot of bother with this.
I'm writing a view with a bunch of controls in it, and my UIViewController is getting a bit bloated with all the delegate protocols it has to support. So I want to break out some of the delegation to separate classes. But there doesn't seem to be any way of connecting the view controls to the separated classes in the storyboard?
Example: I did have:
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource{
...code...
}
and it was easy to connect the picker view to this by dragging the connectors to the ViewController thingy in the top margin of the view in the storyboard, like so:
now I have:
class ViewController: UIViewController{
...code...
}
and in a separate file:
class PickerHandler: UIPickerViewDelegate, UIPickerViewDataSource{
...code...
}
However, there's no way I can drag the delegate connectors for the picker view to this class, so how do I wire up the delegate?
Quick answers: Like snowman4415.
Swift recommended way:
class ViewController: UIViewController {
...code...
}
extension ViewController: UIPickerViewDelegate {
}
extension ViewController: UIPickerViewDataSource {
}
You can put these extensions anywhere you want. There is no need to make another class for this, assuming you just don't want your class becomes too big.
Also, there are other cases that require making another class, like you did, but it requires more works, like when you have to write extra protocol in order to delegate from PickerHandler back to ViewController (because you can't call ViewController's instance inside PickerHandler class directly)
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!