Is there a way to overload a method in a protocol? - swift

I'd like to expand the functionality of a delegate that I've been using to accept a two different types as the second parameter. When I attempt to add an overloaded method, I get two errors:
So my question is, Is there a way to overload a method in a protocol in swift to allow different parameters?
Error 1
Type 'ViewController' does not conform to protocol 'myCellDelegate'
Error 2
Cannot assign a value of type 'ViewController' to a value of type 'myCellDelegate?'
myCellDelegate.swift
protocol myCellDelegate {
func didChangeState(# sender: SettingCell, isOn: Bool)
func didChangeState(# sender: SettingCell, time: Int) // error
}
(in ViewController.Swift)
class ViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate, myCellDelegate {
cellForRowAtIndexPath
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CustomSettingCell") as! SettingCell
let section = sectionNames[0]
let logItem = logItems[indexPath.row]
cell.settingsLabel?.text = logItem.settingLabel
cell.settingsSwitch.on = logItem.switchState
cell.cellDelegate = self
return cell
}
Usage
func didChangeState(#sender: SettingCell, isOn: Bool) {
...
}

The immediate answer is YES there is a way to overload a method in a protocol, and I was pretty much there had I only listened to the error messages.
I simply forgot to implement the method in my ViewController. Although its obvious to me now, it wasn't apparent to me at the time because the method name was the same.
So the final code should look like this:
myCellDelegate.swift
protocol myCellDelegate {
func didChangeState(# sender: SettingCell, isOn: Bool)
func didChangeState(# sender: SettingCell, time: Int)
}
ViewController.swift
class ViewController: UITableViewController, UITableViewDelegate, myCellDelegate {
func didChangeState(#sender: SettingCell, isOn: Bool) {
...
}
func didChangeState(#sender: SettingCell, time: Int) {
...
}
}
As #woodstock suggested in the OP, this is probably a good time to use a generic type rather than an overloaded method.

Related

Getting 'cannot convert value of type 'IndexPath.Type' to expected argument type 'IndexPath'

I'm trying to make a button do something in another view controller. I made a delegate, added a function with a requirement of having an indexPath so I could use it to delete items in an array, but when I make a variable indexPath equal to IndexPath.self and try feeding it into the actual IBAction, it spits out the error shown in the title. Here is my code for my first view controller, where the IBAction and protocol is defined.
import UIKit
protocol ToDoItemCellDelegate: AnyObject {
func didTapX(with indexPath: IndexPath)
}
class ToDoItemCell: UITableViewCell {
weak var delegate: ToDoItemCellDelegate?
static let identifier = "ToDoItemCell"
static func nib() -> UINib {
return UINib(nibName: "ToDoItemCell", bundle: nil)
}
#IBOutlet var xButton: UIButton!
private var indexPath = IndexPath.self
#IBAction func didTapX(_ sender: UIButton) {
delegate?.didTapX(with: indexPath)
}
override func awakeFromNib() {
super.awakeFromNib()
}
}
You have mistaken type and entity. Your delegate expects an entity and your var only provides a type. You stated in your comment that you do not need to provide any meaningfull value here so you have multiple possibilities:
Remove the argument from the delegate:
protocol ToDoItemCellDelegate: AnyObject {
func didTapX()
}
or create a default value while calling the delegate and remove the property:
#IBAction func didTapX(_ sender: UIButton) {
delegate?.didTapX(with: IndexPath(item: 0, section: 0))
}
or assign a defalut to the property and use it while calling the delegate:
private var indexPath = IndexPath(item: 0, section: 0)

Delegate not get called

protocol WeatherManagerDelegate {
func didUpdateWeater(weather: ConsolidatedWeather)
func didFailWithError(error: Error)
}
ViewController: where i am setting value didSelectRowAt and using performSegue going to another viewController
class WeatherListViewController: UIViewController {
var delegate: WeatherManagerDelegate?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let index = weatherViewModel.didSelect(at: indexPath.row)
self.delegate?.didUpdateWeater(weather: index)
performSegue(withIdentifier: K.DetailsView.segueIndentifier, sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVc = segue.destination as! DetailsViewController
}
}
this is my ViewModel calss: from my ViewModel, I will send value to ViewController and Update UI
class DetailsWeatherViewModel{
var w = WeatherListViewController()
func a(){
print("aaa")
w.delegate = self
}
}
extension DetailsWeatherViewModel: WeatherManagerDelegate{
func didUpdateWeater(weather: ConsolidatedWeather) {
weatherData = weather
print("weatherData: \(String(describing: weatherData))")
}
func didFailWithError(error: Error) {
print(error)
}
}
what I am doing wrong...????
You should be careful of memory leaks when using delegate pattern. I think you can solve this problem by making protocol limit to class and declare property by weak var. Although WeatherListViewController disappeared, WeatherListViewController and DetailsWeatherViewModel are not likely to be deinit unless you use weak reference. Try this.
protocol WeatherManagerDelegate : class {
func didUpdateWeater(weather: ConsolidatedWeather)
func didFailWithError(error: Error)
}
weak var delegate: WeatherManagerDelegate?
If you are following MVVM architecture then you can create a viewModel object inside your viewcontroller and then use the updated values in VM directly using VM object.
Else if you want to use delegate then you need to write the protocols in viewModel and use it in VC. You shouldn't be creating Viewcontroller object inside the Viewmodel.

removing the duplicating code code from cell delegate

I have a tableview, to configure a cell (from VC),
cell.model = dataSource[indexpath.row]
In the didSet of cell.model, I am initialising the cell contents.
Cell has 3 buttons, tapping on which, I am informing the VC through CellDelegate
protocol CellDelegate {
func didTapButton1(model: Model)
func didTapButton2(model: Model)
func didTapButton3(model: Model)
}
My concern:-
I don't want to pass the model here (as it is already associated with the Cell - somehow need to fetch the model from cell)
I would like to call didTapButton() without the parameter. Then in the VC,
extension VC: CellDelegate {
//I need to fetch the model associated with the cell.
func didTapButton1() { }
func didTapButton2() { }
func didTapButton3() { }
}
I could achieve this using closure, but it is not preferred here.
Any help would be appreciated.*
I'm guessing the reason why you don't want to pass the model is because having a model in all three methods look like code duplication. Well, if you have look at the delegates in the framework, such as UITableViewDelegate, UITextFieldDelegate, most, if not all, of them accept the thing that they are a delegate of as the first parameter. All the methods in UITableViewDelegate has a tableView parameter. Therefore, it would be OK for you to follow the pattern as well:
protocol CellDelegate {
func didTapButton1(_ cell: Cell)
func didTapButton2(_ cell: Cell)
func didTapButton3(_ cell: Cell)
}
Personally, I would write only one method in this delegate:
protocol CellDelegate {
func didTapButton(_ cell: Cell, buttonNumber: Int)
}
In the VC extension, you simply check buttonNumber to see which button is pressed:
switch buttonNumber {
case 1: button1Tapped()
case 2: button2Tapped()
case 3: button3Tapped()
default: fatalError()
}
// ...
func button1Tapped() { ... }
func button2Tapped() { ... }
func button3Tapped() { ... }

How to set delegate in a protocol extension

I have multiple view controllers which shows same kind of cells. I want to set delegate in a protocol extension like this:
class ProductsViewController: UIViewController, ProductShowcase {
//other properties
#IBOutlet weak var productCollectionView: UICollectionView!
var dataSource: DataSource!
override func viewDidLoad() {
super.viewDidLoad()
setupDataSource()
setupCollectionView()
}
func didSelectProduct(product: Product) {
print(product)
}
//other functions
}
protocol ProductShowcase: UICollectionViewDelegate {
var dataSource: DataSource! { get set }
var productCollectionView: UICollectionView! { get }
func didSelectProduct(product: Product)
}
extension ProductShowcase {
func setupCollectionView() {
productCollectionView.registerClass(ProductCollectionViewCell.self, forCellWithReuseIdentifier: "productCell")
productCollectionView.dataSource = dataSource
print(self) //prints ProductsViewController
productCollectionView.delegate = self //
print(productCollectionView.delegate) //prints optional ProductsViewController
}
}
extension ProductShowcase {
//this delegate method is not called
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
didSelectProduct(dataSource.dataObjects[indexPath.row])
}
}
When didSelectItemAtIndexPath is implemented in ProductsViewController it gets called. Is there something I missed or is this a wrong approach?
It is a Objective-C interoperability limitation. You are not allowed to implement protocols with optionals function in protocol extension like you wanted (protocols which are from Objective-C type UIKit control's delegates and datasources, etc.). You can have default implementation of only protocol that are written like:
// No, #objc in the front of protocol. (i.e. objc-type protocol)
protocol X {
}

Extend UICollectionViewDataSource Protocol to add default implementations

I have a fairly big application which has a lot of collection views. Most of the collection view have same implementations for Data Source and the Flow Layout Delegate (same sizes, margins etc). I am trying to create a single protocol which provides the default implementations of UICollectionViewDataSource and UICollectionViewDelegateFlowLayout. Here is my code.
protocol TiledCollectionView{}
extension UICollectionViewDataSource where Self: TiledCollectionView{
//default implementation of the 3 methods to load the data ...
}
extension UICollectionViewDelegateFlowLayout where Self: TiledCollectionView {
//default implementation for layout methods to set default margins etc...
}
class MyViewController: UIViewController, TiledCollectionView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
// the rest of the required logic for view controller
// here I Don't implement any CollectionView methods since I have provided the default implementation already
}
The problem is that, the compiler complains that MyViewController does not conform to UICollectionViewDataSource. This should not be the case because I am clearly saying that add the default implementations if the type is TiledCollectionView.
Can some one help?
I know it's not exactly what you asked, I tried - it didn't work. Now looking for possible answer, because had similiar situation. But I can offer you such on option how to hide in your custom protocol all the logic for delegate/dataSource implementation.
class CollectionViewProtocolHandler: NSObject, UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 0
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
return UICollectionViewCell() // only for test
}
}
protocol CollectionViewProtocol {
var handler: CollectionViewProtocolHandler! {get set}
mutating func useProtocolForCollectionView(collectionView: UICollectionView)
}
extension CollectionViewProtocol {
mutating func useProtocolForCollectionView(collectionView: UICollectionView) {
handler = CollectionViewProtocolHandler()
collectionView.delegate = handler
collectionView.dataSource = handler
}
}
class ViewController: UIViewController, CollectionViewProtocol {
var handler: CollectionViewProtocolHandler! // CollectionViewProtocol convenience
override func viewDidLoad() {
super.viewDidLoad()
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: UICollectionViewFlowLayout())
collectionView.backgroundColor = .redColor()
view.addSubview(collectionView)
var reference = self
reference.useProtocolForCollectionView(collectionView) // for initialize protocol
}
}
I expect the problem is that this is an Objective-C protocol. Objective-C has never heard of a protocol extension. Therefore it has no knowledge that this protocol extension is injecting two functions into MyClass. It can't see them, and so as far as it is concerned, the protocol requirements are not satisfied.
To add to, but modify, what katleta3000 answered with, you can restrict a protocol to only apply to a 'class'
CollectionViewProtocol : class
so that you don't need 'useProtocolForCollectionView:' to be mutating
Which then makes it so you don't need that var reference = self and you can just say self.userProtocolForCollectionView(collectionView)
Especially if you only plan on implementing this protocol only with NSObject's or class types (UIViewController, UICollectionView, etc.)